void unregister_c_can_dev(struct net_device *dev) { struct c_can_priv *priv = netdev_priv(dev); /* disable all interrupts */ c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); unregister_candev(dev); }
static void c_can_stop(struct net_device *dev) { struct c_can_priv *priv = netdev_priv(dev); /* disable all interrupts */ c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); /* set the state as STOPPED */ priv->can.state = CAN_STATE_STOPPED; }
static void c_can_start(struct net_device *dev) { struct c_can_priv *priv = netdev_priv(dev); /* basic c_can configuration */ c_can_chip_config(dev); priv->can.state = CAN_STATE_ERROR_ACTIVE; /* reset tx helper pointers */ priv->tx_next = priv->tx_echo = 0; /* enable status change, error and module interrupts */ c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS); }
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) return IRQ_NONE; /* disable all interrupts and schedule the NAPI */ c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); napi_schedule(&priv->napi); return IRQ_HANDLED; }
static int c_can_handle_state_change(struct net_device *dev, enum c_can_bus_error_types error_type) { unsigned int reg_err_counter; unsigned int rx_err_passive; struct c_can_priv *priv = netdev_priv(dev); struct net_device_stats *stats = &dev->stats; struct can_frame *cf; struct sk_buff *skb; struct can_berr_counter bec; /* propagate the error condition to the CAN stack */ skb = alloc_can_err_skb(dev, &cf); if (unlikely(!skb)) return 0; c_can_get_berr_counter(dev, &bec); reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); rx_err_passive = (reg_err_counter & ERR_CNT_RP_MASK) >> ERR_CNT_RP_SHIFT; switch (error_type) { case C_CAN_ERROR_WARNING: /* error warning state */ priv->can.can_stats.error_warning++; priv->can.state = CAN_STATE_ERROR_WARNING; cf->can_id |= CAN_ERR_CRTL; cf->data[1] = (bec.txerr > bec.rxerr) ? CAN_ERR_CRTL_TX_WARNING : CAN_ERR_CRTL_RX_WARNING; cf->data[6] = bec.txerr; cf->data[7] = bec.rxerr; break; case C_CAN_ERROR_PASSIVE: /* error passive state */ priv->can.can_stats.error_passive++; priv->can.state = CAN_STATE_ERROR_PASSIVE; cf->can_id |= CAN_ERR_CRTL; if (rx_err_passive) cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; if (bec.txerr > 127) cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; cf->data[6] = bec.txerr; cf->data[7] = bec.rxerr; break; case C_CAN_BUS_OFF: /* bus-off state */ priv->can.state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; /* * disable all interrupts in bus-off mode to ensure that * the CPU is not hogged down */ c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); can_bus_off(dev); break; default: break; } netif_receive_skb(skb); stats->rx_packets++; stats->rx_bytes += cf->can_dlc; return 1; }
static int c_can_poll(struct napi_struct *napi, int quota) { u16 irqstatus; int lec_type = 0; int work_done = 0; struct net_device *dev = napi->dev; struct c_can_priv *priv = netdev_priv(dev); irqstatus = priv->irqstatus; if (!irqstatus) goto end; /* 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->write_reg(priv, C_CAN_STS_REG, priv->current_status & ~STATUS_TXOK); if (priv->current_status & STATUS_RXOK) 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); } /* 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; } priv->last_status = priv->current_status; /* handle lec errors on the bus */ lec_type = c_can_has_and_handle_berr(priv); if (lec_type) work_done += c_can_handle_bus_err(dev, lec_type); } 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); } end: if (work_done < quota) { napi_complete(napi); /* enable all IRQs */ c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS); } return work_done; }