static void c_can_setup_receive_object(struct net_device *dev, int iface, int objno, unsigned int mask, unsigned int id, unsigned int mcont) { struct c_can_priv *priv = netdev_priv(dev); priv->write_reg(priv, C_CAN_IFACE(MASK1_REG, iface), IFX_WRITE_LOW_16BIT(mask)); /* According to C_CAN documentation, the reserved bit * in IFx_MASK2 register is fixed 1 */ priv->write_reg(priv, C_CAN_IFACE(MASK2_REG, iface), IFX_WRITE_HIGH_16BIT(mask) | BIT(13)); priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), IFX_WRITE_LOW_16BIT(id)); priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), (IF_ARB_MSGVAL | IFX_WRITE_HIGH_16BIT(id))); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), mcont); c_can_object_put(dev, iface, objno, IF_COMM_ALL & ~IF_COMM_TXRQST); netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, c_can_read_reg32(priv, C_CAN_MSGVAL1_REG)); }
static void c_can_write_msg_object(struct net_device *dev, int iface, struct can_frame *frame, int objno) { int i; u16 flags = 0; unsigned int id; struct c_can_priv *priv = netdev_priv(dev); if (!(frame->can_id & CAN_RTR_FLAG)) flags |= IF_ARB_TRANSMIT; if (frame->can_id & CAN_EFF_FLAG) { id = frame->can_id & CAN_EFF_MASK; flags |= IF_ARB_MSGXTD; } else id = ((frame->can_id & CAN_SFF_MASK) << 18); flags |= IF_ARB_MSGVAL; priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), IFX_WRITE_LOW_16BIT(id)); priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), flags | IFX_WRITE_HIGH_16BIT(id)); for (i = 0; i < frame->can_dlc; i += 2) { priv->write_reg(priv, C_CAN_IFACE(DATA1_REG, iface) + i / 2, frame->data[i] | (frame->data[i + 1] << 8)); } /* enable interrupt for this message object */ priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), IF_MCONT_TXIE | IF_MCONT_TXRQST | IF_MCONT_EOB | frame->can_dlc); c_can_object_put(dev, iface, objno, IF_COMM_ALL); }
static void c_can_inval_msg_object(struct net_device *dev, int iface, int obj) { struct c_can_priv *priv = netdev_priv(dev); priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), 0); priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), 0); c_can_inval_tx_object(dev, iface, obj); }
static void c_can_setup_tx_object(struct net_device *dev, int iface, struct can_frame *frame, int idx) { struct c_can_priv *priv = netdev_priv(dev); u16 ctrl = IF_MCONT_TX | frame->can_dlc; bool rtr = frame->can_id & CAN_RTR_FLAG; u32 arb = IF_ARB_MSGVAL; int i; if (frame->can_id & CAN_EFF_FLAG) { arb |= frame->can_id & CAN_EFF_MASK; arb |= IF_ARB_MSGXTD; } else { arb |= (frame->can_id & CAN_SFF_MASK) << 18; } if (!rtr) arb |= IF_ARB_TRANSMIT; /* * If we change the DIR bit, we need to invalidate the buffer * first, i.e. clear the MSGVAL flag in the arbiter. */ if (rtr != (bool)test_bit(idx, &priv->tx_dir)) { u32 obj = idx + C_CAN_MSG_OBJ_TX_FIRST; c_can_inval_msg_object(dev, iface, obj); change_bit(idx, &priv->tx_dir); } priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), arb); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl); if (priv->type == BOSCH_D_CAN) { u32 data = 0, dreg = C_CAN_IFACE(DATA1_REG, iface); for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) { data = (u32)frame->data[i]; data |= (u32)frame->data[i + 1] << 8; data |= (u32)frame->data[i + 2] << 16; data |= (u32)frame->data[i + 3] << 24; priv->write_reg32(priv, dreg, data); } } else { for (i = 0; i < frame->can_dlc; i += 2) { priv->write_reg(priv, C_CAN_IFACE(DATA1_REG, iface) + i / 2, frame->data[i] | (frame->data[i + 1] << 8)); } } }
static void c_can_inval_msg_object(struct net_device *dev, int iface, int objno) { struct c_can_priv *priv = netdev_priv(dev); priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), 0); priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), 0); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), 0); c_can_object_put(dev, iface, objno, IF_COMM_ARB | IF_COMM_CONTROL); netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, c_can_read_reg32(priv, C_CAN_MSGVAL1_REG)); }
static void c_can_setup_receive_object(struct net_device *dev, int iface, u32 obj, u32 mask, u32 id, u32 mcont) { struct c_can_priv *priv = netdev_priv(dev); mask |= BIT(29); priv->write_reg32(priv, C_CAN_IFACE(MASK1_REG, iface), mask); id |= IF_ARB_MSGVAL; priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), id); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), mcont); c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP); }
static int c_can_handle_lost_msg_obj(struct net_device *dev, int iface, int objno, u32 ctrl) { struct net_device_stats *stats = &dev->stats; struct c_can_priv *priv = netdev_priv(dev); struct can_frame *frame; struct sk_buff *skb; ctrl &= ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl); c_can_object_put(dev, iface, objno, IF_COMM_CONTROL); stats->rx_errors++; stats->rx_over_errors++; /* create an error msg */ skb = alloc_can_err_skb(dev, &frame); if (unlikely(!skb)) return 0; frame->can_id |= CAN_ERR_CRTL; frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; netif_receive_skb(skb); return 1; }
/* * Note: According to documentation clearing TXIE while MSGVAL is set * is not allowed, but works nicely on C/DCAN. And that lowers the I/O * load significantly. */ static void c_can_inval_tx_object(struct net_device *dev, int iface, int obj) { struct c_can_priv *priv = netdev_priv(dev); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), 0); c_can_object_put(dev, iface, obj, IF_COMM_INVAL); }
/* * theory of operation: * * priv->tx_echo holds the number of the oldest can_frame put for * transmission into the hardware, but not yet ACKed by the CAN tx * complete IRQ. * * We iterate from priv->tx_echo to priv->tx_next and check if the * packet has been transmitted, echo it back to the CAN framework. * If we discover a not yet transmitted packet, stop looking for more. */ static void c_can_do_tx(struct net_device *dev) { u32 val; u32 msg_obj_no; struct c_can_priv *priv = netdev_priv(dev); struct net_device_stats *stats = &dev->stats; for (/* nix */; (priv->tx_next - priv->tx_echo) > 0; priv->tx_echo++) { msg_obj_no = get_tx_echo_msg_obj(priv); val = c_can_read_reg32(priv, C_CAN_TXRQST1_REG); if (!(val & (1 << (msg_obj_no - 1)))) { can_get_echo_skb(dev, msg_obj_no - C_CAN_MSG_OBJ_TX_FIRST); stats->tx_bytes += priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, 0)) & IF_MCONT_DLC_MASK; stats->tx_packets++; can_led_event(dev, CAN_LED_EVENT_TX); c_can_inval_msg_object(dev, 0, msg_obj_no); } else { break; } } /* restart queue if wrap-up or if queue stalled on last pkt */ if (((priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) != 0) || ((priv->tx_echo & C_CAN_NEXT_MSG_OBJ_MASK) == 0)) netif_wake_queue(dev); }
static void c_can_handle_lost_msg_obj(struct net_device *dev, int iface, int objno) { struct c_can_priv *priv = netdev_priv(dev); struct net_device_stats *stats = &dev->stats; struct sk_buff *skb; struct can_frame *frame; netdev_err(dev, "msg lost in buffer %d\n", objno); c_can_object_get(dev, iface, objno, IF_COMM_ALL & ~IF_COMM_TXRQST); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), IF_MCONT_CLR_MSGLST); c_can_object_put(dev, 0, objno, IF_COMM_CONTROL); /* create an error msg */ skb = alloc_can_err_skb(dev, &frame); if (unlikely(!skb)) return; frame->can_id |= CAN_ERR_CRTL; frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; stats->rx_errors++; stats->rx_over_errors++; netif_receive_skb(skb); }
static int c_can_read_msg_object(struct net_device *dev, int iface, int ctrl) { u16 flags, data; int i; unsigned int val; struct c_can_priv *priv = netdev_priv(dev); struct net_device_stats *stats = &dev->stats; struct sk_buff *skb; struct can_frame *frame; skb = alloc_can_skb(dev, &frame); if (!skb) { stats->rx_dropped++; return -ENOMEM; } frame->can_dlc = get_can_dlc(ctrl & 0x0F); flags = priv->read_reg(priv, C_CAN_IFACE(ARB2_REG, iface)); val = priv->read_reg(priv, C_CAN_IFACE(ARB1_REG, iface)) | (flags << 16); if (flags & IF_ARB_MSGXTD) frame->can_id = (val & CAN_EFF_MASK) | CAN_EFF_FLAG; else frame->can_id = (val >> 18) & CAN_SFF_MASK; if (flags & IF_ARB_TRANSMIT) frame->can_id |= CAN_RTR_FLAG; else { for (i = 0; i < frame->can_dlc; i += 2) { data = priv->read_reg(priv, C_CAN_IFACE(DATA1_REG, iface) + i / 2); frame->data[i] = data; frame->data[i + 1] = data >> 8; } } netif_receive_skb(skb); stats->rx_packets++; stats->rx_bytes += frame->can_dlc; can_led_event(dev, CAN_LED_EVENT_RX); return 0; }
static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl) { struct net_device_stats *stats = &dev->stats; struct c_can_priv *priv = netdev_priv(dev); struct can_frame *frame; struct sk_buff *skb; u32 arb, data; skb = alloc_can_skb(dev, &frame); if (!skb) { stats->rx_dropped++; return -ENOMEM; } frame->can_dlc = get_can_dlc(ctrl & 0x0F); arb = priv->read_reg32(priv, C_CAN_IFACE(ARB1_REG, iface)); if (arb & IF_ARB_MSGXTD) frame->can_id = (arb & CAN_EFF_MASK) | CAN_EFF_FLAG; else frame->can_id = (arb >> 18) & CAN_SFF_MASK; if (arb & IF_ARB_TRANSMIT) { frame->can_id |= CAN_RTR_FLAG; } else { int i, dreg = C_CAN_IFACE(DATA1_REG, iface); if (priv->type == BOSCH_D_CAN) { for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) { data = priv->read_reg32(priv, dreg); frame->data[i] = data; frame->data[i + 1] = data >> 8; frame->data[i + 2] = data >> 16; frame->data[i + 3] = data >> 24; } } else { for (i = 0; i < frame->can_dlc; i += 2, dreg++) { data = priv->read_reg(priv, dreg); frame->data[i] = data; frame->data[i + 1] = data >> 8; } } }
/* * theory of operation: * * c_can core saves a received CAN message into the first free message * object it finds free (starting with the lowest). Bits NEWDAT and * INTPND are set for this message object indicating that a new message * has arrived. To work-around this issue, we keep two groups of message * objects whose partitioning is defined by C_CAN_MSG_OBJ_RX_SPLIT. * * To ensure in-order frame reception we use the following * approach while re-activating a message object to receive further * frames: * - if the current message object number is lower than * C_CAN_MSG_RX_LOW_LAST, do not clear the NEWDAT bit while clearing * the INTPND bit. * - if the current message object number is equal to * C_CAN_MSG_RX_LOW_LAST then clear the NEWDAT bit of all lower * receive message objects. * - if the current message object number is greater than * C_CAN_MSG_RX_LOW_LAST then clear the NEWDAT bit of * only this message object. */ static int c_can_do_rx_poll(struct net_device *dev, int quota) { u32 num_rx_pkts = 0; unsigned int msg_obj, msg_ctrl_save; struct c_can_priv *priv = netdev_priv(dev); u32 val = c_can_read_reg32(priv, C_CAN_INTPND1_REG); for (msg_obj = C_CAN_MSG_OBJ_RX_FIRST; msg_obj <= C_CAN_MSG_OBJ_RX_LAST && quota > 0; val = c_can_read_reg32(priv, C_CAN_INTPND1_REG), msg_obj++) { /* * as interrupt pending register's bit n-1 corresponds to * message object n, we need to handle the same properly. */ if (val & (1 << (msg_obj - 1))) { c_can_object_get(dev, 0, msg_obj, IF_COMM_ALL & ~IF_COMM_TXRQST); msg_ctrl_save = priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, 0)); if (msg_ctrl_save & IF_MCONT_EOB) return num_rx_pkts; if (msg_ctrl_save & IF_MCONT_MSGLST) { c_can_handle_lost_msg_obj(dev, 0, msg_obj); num_rx_pkts++; quota--; continue; } if (!(msg_ctrl_save & IF_MCONT_NEWDAT)) continue; /* read the data from the message object */ c_can_read_msg_object(dev, 0, msg_ctrl_save); if (msg_obj < C_CAN_MSG_RX_LOW_LAST) c_can_mark_rx_msg_obj(dev, 0, msg_ctrl_save, msg_obj); else if (msg_obj > C_CAN_MSG_RX_LOW_LAST) /* activate this msg obj */ c_can_activate_rx_msg_obj(dev, 0, msg_ctrl_save, msg_obj); else if (msg_obj == C_CAN_MSG_RX_LOW_LAST) /* activate all lower message objects */ c_can_activate_all_lower_rx_msg_obj(dev, 0, msg_ctrl_save); num_rx_pkts++; quota--; } } return num_rx_pkts; }
static inline void c_can_object_put(struct net_device *dev, int iface, int objno, int mask) { struct c_can_priv *priv = netdev_priv(dev); /* * As per specs, after writting the message object number in the * IF command request register the transfer b/w interface * register and message RAM must be complete in 6 CAN-CLK * period. */ priv->write_reg(priv, C_CAN_IFACE(COMMSK_REG, iface), (IF_COMM_WR | IFX_WRITE_LOW_16BIT(mask))); priv->write_reg(priv, C_CAN_IFACE(COMREQ_REG, iface), IFX_WRITE_LOW_16BIT(objno)); if (c_can_msg_obj_is_busy(priv, iface)) netdev_err(dev, "timed out in object put\n"); }
static inline void c_can_activate_rx_msg_obj(struct net_device *dev, int iface, int ctrl_mask, int obj) { struct c_can_priv *priv = netdev_priv(dev); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl_mask & ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT)); c_can_object_put(dev, iface, obj, IF_COMM_CONTROL); }
static inline void c_can_activate_all_lower_rx_msg_obj(struct net_device *dev, int iface, int ctrl_mask) { int i; struct c_can_priv *priv = netdev_priv(dev); for (i = C_CAN_MSG_OBJ_RX_FIRST; i <= C_CAN_MSG_RX_LOW_LAST; i++) { priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl_mask & ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT)); c_can_object_put(dev, iface, i, IF_COMM_CONTROL); } }
static void c_can_obj_update(struct net_device *dev, int iface, u32 cmd, u32 obj) { struct c_can_priv *priv = netdev_priv(dev); int cnt, reg = C_CAN_IFACE(COMREQ_REG, iface); priv->write_reg32(priv, reg, (cmd << 16) | obj); for (cnt = MIN_TIMEOUT_VALUE; cnt; cnt--) { if (!(priv->read_reg(priv, reg) & IF_COMR_BUSY)) return; udelay(1); } netdev_err(dev, "Updating object timed out\n"); }
static inline int c_can_msg_obj_is_busy(struct c_can_priv *priv, int iface) { int count = MIN_TIMEOUT_VALUE; while (count && priv->read_reg(priv, C_CAN_IFACE(COMREQ_REG, iface)) & IF_COMR_BUSY) { count--; udelay(1); } if (!count) return 1; return 0; }
static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv, u32 pend, int quota) { u32 pkts = 0, ctrl, obj; while ((obj = ffs(pend)) && quota > 0) { pend &= ~BIT(obj - 1); c_can_rx_object_get(dev, priv, obj); ctrl = priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, IF_RX)); if (ctrl & IF_MCONT_MSGLST) { int n = c_can_handle_lost_msg_obj(dev, IF_RX, obj, ctrl); pkts += n; quota -= n; continue; } /* * This really should not happen, but this covers some * odd HW behaviour. Do not remove that unless you * want to brick your machine. */ if (!(ctrl & IF_MCONT_NEWDAT)) continue; /* read the data from the message object */ c_can_read_msg_object(dev, IF_RX, ctrl); c_can_rx_finalize(dev, priv, obj); pkts++; quota--; } return pkts; }