/*
- * $Id$
+ * CAN bus driver for the alone generic (as possible as) MSCAN controller.
*
- * mscan.c
+ * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
*
- * DESCRIPTION:
- * CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
*
- * AUTHOR:
- * Andrey Volkov <avolkov@varma-el.com>
- *
- * COPYRIGHT:
- * 2005-2006, Varma Electronics Oy
- *
- * LICENCE:
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/autoconf.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
-#include <linux/can/can.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
#include <linux/list.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/error.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
#include <asm/io.h>
+#endif
-#include <linux/can/dev.h>
#include "mscan.h"
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
#define MSCAN_NORMAL_MODE 0
-#define MSCAN_SLEEP_MODE MSCAN_SLPRQ
+#define MSCAN_SLEEP_MODE MSCAN_SLPRQ
#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ)
#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ)
-
-#define BTR0_BRP_MASK 0x3f
-#define BTR0_SJW_SHIFT 6
-#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT)
-
-#define BTR1_TSEG1_MASK 0xf
-#define BTR1_TSEG2_SHIFT 4
-#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT)
-#define BTR1_SAM_SHIFT 7
-
-#define BTR0_SET_BRP(brp) (((brp)-1)&BTR0_BRP_MASK)
-#define BTR0_SET_SJW(sjw) ((((sjw)-1)<<BTR0_SJW_SHIFT)&BTR0_SJW_MASK)
-
-#define BTR1_SET_TSEG1(tseg1) (((tseg1)-1)&BTR1_TSEG1_MASK)
-#define BTR1_SET_TSEG2(tseg2) ((((tseg2)-1)<<BTR1_TSEG2_SHIFT)&BTR1_TSEG2_MASK)
-#define BTR1_SET_SAM(sam) (((sam)&1)<<BTR1_SAM_SHIFT)
+#define MSCAN_SET_MODE_RETRIES 255
+#define MSCAN_ECHO_SKB_MAX 3
+
+#define BTR0_BRP_MASK 0x3f
+#define BTR0_SJW_SHIFT 6
+#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT)
+
+#define BTR1_TSEG1_MASK 0xf
+#define BTR1_TSEG2_SHIFT 4
+#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT)
+#define BTR1_SAM_SHIFT 7
+
+#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK)
+#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \
+ BTR0_SJW_MASK)
+
+#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK)
+#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \
+ BTR1_TSEG2_MASK)
+#define BTR1_SET_SAM(sam) ((sam) ? 1 << BTR1_SAM_SHIFT : 0)
+
+static struct can_bittiming_const mscan_bittiming_const = {
+ .name = "mscan",
+ .tseg1_min = 4,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
struct mscan_state {
u8 mode;
#define TX_QUEUE_SIZE 3
-typedef struct {
+struct tx_queue_entry {
struct list_head list;
u8 mask;
-} tx_queue_entry_t;
+ u8 id;
+};
struct mscan_priv {
- volatile unsigned long flags;
+ struct can_priv can;
+ long open_time;
+ unsigned long flags;
u8 shadow_statflg;
u8 shadow_canrier;
u8 cur_pri;
+ u8 prev_buf_id;
u8 tx_active;
struct list_head tx_head;
- tx_queue_entry_t tx_queue[TX_QUEUE_SIZE];
+ struct tx_queue_entry tx_queue[TX_QUEUE_SIZE];
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ struct napi_struct napi;
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ struct napi_struct napi;
+ struct net_device *dev;
+#endif
};
#define F_RX_PROGRESS 0
#define F_TX_PROGRESS 1
#define F_TX_WAIT_ALL 2
-static int mscan_set_mode(struct can_device *can, u8 mode)
+static enum can_state state_map[] = {
+ CAN_STATE_ERROR_ACTIVE,
+ CAN_STATE_ERROR_WARNING,
+ CAN_STATE_ERROR_PASSIVE,
+ CAN_STATE_BUS_OFF
+};
+
+static int mscan_set_mode(struct net_device *dev, u8 mode)
{
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
int ret = 0;
int i;
u8 canctl1;
- if( mode != MSCAN_NORMAL_MODE ) {
+ if (mode != MSCAN_NORMAL_MODE) {
+
+ if (priv->tx_active) {
+ /* Abort transfers before going to sleep */#
+ out_8(®s->cantarq, priv->tx_active);
+ /* Suppress TX done interrupts */
+ out_8(®s->cantier, 0);
+ }
+
canctl1 = in_8(®s->canctl1);
if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
- out_8( ®s->canctl0, in_8(®s->canctl0) | MSCAN_SLPRQ );
- for (i = 0; i < 255; i++ ) {
- if ( in_8(®s->canctl1) & MSCAN_SLPAK )
+ out_8(®s->canctl0,
+ in_8(®s->canctl0) | MSCAN_SLPRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(®s->canctl1) & MSCAN_SLPAK)
break;
udelay(100);
}
- if(i>255)
- ret=-ENODEV;
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
}
-
- if ( !ret && (mode & MSCAN_INITRQ) && (canctl1 & MSCAN_INITAK) == 0) {
- out_8( ®s->canctl0, in_8( ®s->canctl0 ) | MSCAN_INITRQ);
- for (i = 0; i < 255; i++ ) {
- if ( in_8(®s->canctl1) & MSCAN_INITAK )
+ if (!ret)
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ if (!ret && (mode & MSCAN_INITRQ)
+ && (canctl1 & MSCAN_INITAK) == 0) {
+ out_8(®s->canctl0,
+ in_8(®s->canctl0) | MSCAN_INITRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(®s->canctl1) & MSCAN_INITAK)
break;
}
- if(i>255)
- ret=-ENODEV;
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
}
+ if (!ret)
+ priv->can.state = CAN_STATE_STOPPED;
- if ( !ret && (mode & MSCAN_CSWAI) )
- out_8( ®s->canctl0, in_8(®s->canctl0) | MSCAN_CSWAI);
+ if (!ret && (mode & MSCAN_CSWAI))
+ out_8(®s->canctl0,
+ in_8(®s->canctl0) | MSCAN_CSWAI);
- } else {
+ } else {
canctl1 = in_8(®s->canctl1);
- if ( canctl1 & (MSCAN_SLPAK | MSCAN_INITAK) ) {
+ if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
out_8(®s->canctl0, in_8(®s->canctl0) &
~(MSCAN_SLPRQ | MSCAN_INITRQ));
- for (i = 0; i < 255; i++ ) {
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
canctl1 = in_8(®s->canctl1);
- if ( (canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)) ==0 )
+ if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)))
break;
}
- if(i>255)
- ret=-ENODEV;
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ else
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
}
}
return ret;
}
-static void mscan_push_state(struct can_device *can, struct mscan_state *state)
+static int mscan_start(struct net_device *dev)
{
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 canrflg;
+ int err;
- state->mode = in_8(®s->canctl0) &
- (MSCAN_SLPRQ | MSCAN_INITRQ | MSCAN_CSWAI);
- state->canrier = in_8(®s->canrier);
- state->cantier = in_8(®s->cantier);
-}
+ out_8(®s->canrier, 0);
-static int mscan_pop_state(struct can_device *can, struct mscan_state *state)
-{
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
- int ret;
- ret = mscan_set_mode(can, state->mode);
- if (!ret) {
- out_8( ®s->canrier, state->canrier);
- out_8( ®s->cantier, state->cantier);
- }
- return ret;
+ INIT_LIST_HEAD(&priv->tx_head);
+ priv->prev_buf_id = 0;
+ priv->cur_pri = 0;
+ priv->tx_active = 0;
+ priv->shadow_canrier = 0;
+ priv->flags = 0;
+
+ err = mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+ if (err)
+ return err;
+
+ canrflg = in_8(®s->canrflg);
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ out_8(®s->cantier, 0);
+
+ /* Enable receive interrupts. */
+ out_8(®s->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
+ MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0);
+
+ return 0;
}
-static int mscan_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+static int mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+#else
+static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+#endif
{
struct can_frame *frame = (struct can_frame *)skb->data;
- struct can_device *can = ND2CAN(ndev);
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
- struct mscan_priv *priv = can->priv;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
int i, rtr, buf_id;
u32 can_id;
- if ( frame->can_dlc > 8 )
+ if (frame->can_dlc > 8)
return -EINVAL;
- dev_dbg( ND2D(ndev), "%s\n", __FUNCTION__);
out_8(®s->cantier, 0);
i = ~priv->tx_active & MSCAN_TXE;
buf_id = ffs(i) - 1;
- switch ( hweight8(i) ) {
- case 0:
- netif_stop_queue(ndev);
- dev_err( ND2D(ndev), "BUG! Tx Ring full when queue awake!\n" );
- return NETDEV_TX_BUSY;
- case 1:
- /* if buf_id < 3, then current frame will be send out of order,
- since buffer with lower id have higher priority (hell..) */
- if(buf_id < 3 )
- priv->cur_pri++;
- if(priv->cur_pri==0xff)
+ switch (hweight8(i)) {
+ case 0:
+ netif_stop_queue(dev);
+ dev_err(ND2D(dev), "BUG! Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ case 1:
+ /* if buf_id < 3, then current frame will be send out of order,
+ since buffer with lower id have higher priority (hell..) */
+ netif_stop_queue(dev);
+ case 2:
+ if (buf_id < priv->prev_buf_id) {
+ priv->cur_pri++;
+ if (priv->cur_pri == 0xff) {
set_bit(F_TX_WAIT_ALL, &priv->flags);
- netif_stop_queue(ndev);
- case 2:
- set_bit(F_TX_PROGRESS, &priv->flags);
+ netif_stop_queue(dev);
+ }
+ }
+ set_bit(F_TX_PROGRESS, &priv->flags);
+ break;
}
+ priv->prev_buf_id = buf_id;
out_8(®s->cantbsel, i);
rtr = frame->can_id & CAN_RTR_FLAG;
if (frame->can_id & CAN_EFF_FLAG) {
- dev_dbg(ND2D(ndev), "sending extended frame\n");
-
can_id = (frame->can_id & CAN_EFF_MASK) << 1;
- if( rtr )
+ if (rtr)
can_id |= 1;
out_be16(®s->tx.idr3_2, can_id);
- can_id>>=16;
- can_id = (can_id & 0x7) | ((can_id<<2) & 0xffe0) | (3<<3);
+ can_id >>= 16;
+ can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | (3 << 3);
} else {
- dev_dbg(ND2D(ndev), "sending standard frame\n");
can_id = (frame->can_id & CAN_SFF_MASK) << 5;
- if( rtr )
- can_id |= 1<<4;
+ if (rtr)
+ can_id |= 1 << 4;
}
out_be16(®s->tx.idr1_0, can_id);
- if ( !rtr ) {
- volatile void __iomem *data = ®s->tx.dsr1_0;
- u16 *payload = (u16 *)frame->data;
- /*Its safe to write into dsr[dlc+1]*/
- for (i=0; i<(frame->can_dlc+1)/2; i++) {
+ if (!rtr) {
+ void __iomem *data = ®s->tx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ /*Its safe to write into dsr[dlc+1] */
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
out_be16(data, *payload++);
- data += 2+_MSCAN_RESERVED_DSR_SIZE;
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
}
}
out_8(®s->tx.tbpr, priv->cur_pri);
/* Start transmission. */
- out_8(®s->cantflg, 1<<buf_id);
+ out_8(®s->cantflg, 1 << buf_id);
- if ( ! test_bit(F_TX_PROGRESS, &priv->flags) )
- ndev->trans_start = jiffies;
+ if (!test_bit(F_TX_PROGRESS, &priv->flags))
+ dev->trans_start = jiffies;
- list_add_tail( &priv->tx_queue[buf_id].list, &priv->tx_head);
+ list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
- dev_kfree_skb (skb);
+ can_put_echo_skb(skb, dev, buf_id);
/* Enable interrupt. */
- priv->tx_active |= 1<<buf_id;
- out_8( ®s->cantier, priv->tx_active);
-
- return NETDEV_TX_OK;
-}
-
-
-static void mscan_tx_timeout(struct net_device *ndev)
-{
- struct sk_buff *skb;
- struct can_device *can = ND2CAN(ndev);
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
- struct mscan_priv *priv = can->priv;
- struct can_frame *frame;
- u8 mask;
-
- printk ("%s\n", __FUNCTION__);
-
- out_8(®s->cantier, 0);
-
- mask = list_entry( priv->tx_head.next, tx_queue_entry_t, list)->mask;
- ndev->trans_start = jiffies;
- out_8(®s->cantarq, mask);
+ priv->tx_active |= 1 << buf_id;
out_8(®s->cantier, priv->tx_active);
- skb = dev_alloc_skb(sizeof(struct can_frame));
- if (!skb) {
- if(printk_ratelimit())
- dev_notice(ND2D(ndev), "TIMEOUT packet dropped\n");
- return;
- }
- frame = (struct can_frame *)skb_put(skb,sizeof(struct can_frame));
-
- frame->can_id = CAN_ERR_FLAG | CAN_ERR_TX_TIMEOUT;
- frame->can_dlc = CAN_ERR_DLC;
-
- skb->dev = ndev;
- skb->protocol = __constant_htons(ETH_P_CAN);
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- netif_rx(skb);
-
+ return NETDEV_TX_OK;
}
-static can_state_t state_map[] = {
- CAN_STATE_ACTIVE,
- CAN_STATE_BUS_WARNING,
- CAN_STATE_BUS_PASSIVE,
- CAN_STATE_BUS_OFF
-};
-
-static inline int check_set_state(struct can_device *can, u8 canrflg)
+static inline int check_set_state(struct net_device *dev, u8 canrflg)
{
- can_state_t state;
- int ret = 0;
+ struct mscan_priv *priv = netdev_priv(dev);
+ enum can_state state;
+ int ret = 0;
- if ( !(canrflg & MSCAN_CSCIF) || can->state > CAN_STATE_BUS_OFF)
- return 0;
+ if (!(canrflg & MSCAN_CSCIF) || priv->can.state > CAN_STATE_BUS_OFF)
+ return 0;
- state = state_map[max( MSCAN_STATE_RX(canrflg), MSCAN_STATE_TX(canrflg))];
- if(can->state < state)
- ret = 1;
- if(state == CAN_STATE_BUS_OFF)
- netif_carrier_off(CAN2ND(can));
- else if(can->state == CAN_STATE_BUS_OFF && state != CAN_STATE_BUS_OFF)
- netif_carrier_on(CAN2ND(can));
- can->state = state;
- return ret;
+ state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ if (priv->can.state < state)
+ ret = 1;
+ priv->can.state = state;
+ return ret;
}
-static int mscan_rx_poll(struct net_device *ndev, int *budget)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+static int mscan_rx_poll(struct napi_struct *napi, int quota)
+#else
+static int mscan_rx_poll(struct net_device *dev, int *budget)
+#endif
{
- struct can_device *can = ND2CAN(ndev);
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
- struct mscan_priv *priv = can->priv;
- int npackets = 0, quota = min(ndev->quota, *budget);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
+ struct net_device *dev = napi->dev;
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
+ struct net_device *dev = priv->dev;
+#else
+ struct mscan_priv *priv = netdev_priv(dev);
+ int quota = min(dev->quota, *budget);
+#endif
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ int npackets = 0;
int ret = 1;
struct sk_buff *skb;
struct can_frame *frame;
u32 can_id;
- u8 canrflg;
+ u8 canrflg;
int i;
- while ( npackets < quota &&
- ( (canrflg = in_8(®s->canrflg)) & (MSCAN_RXF | MSCAN_ERR_IF) )) {
+ while (npackets < quota && ((canrflg = in_8(®s->canrflg)) &
+ (MSCAN_RXF | MSCAN_ERR_IF))) {
- skb = dev_alloc_skb(sizeof(struct can_frame));
+ skb = alloc_can_skb(dev, &frame);
if (!skb) {
- if(printk_ratelimit())
- dev_notice(ND2D(ndev), "packet dropped\n");
- can->net_stats.rx_dropped++;
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "packet dropped\n");
+ stats->rx_dropped++;
out_8(®s->canrflg, canrflg);
continue;
}
- frame = (struct can_frame *)skb_put(skb,sizeof(struct can_frame));
-
if (canrflg & MSCAN_RXF) {
- can_id = in_be16( ®s->rx.idr1_0 );
- if (can_id & (1<<3) ) {
- frame->can_id = CAN_EFF_FLAG;
- can_id = (can_id << 16) | in_be16(®s->rx.idr3_2);
- can_id = ((can_id & 0xffe00000) | ((can_id & 0x7ffff) << 2 ))>>2;
- }
- else {
+ can_id = in_be16(®s->rx.idr1_0);
+ if (can_id & (1 << 3)) {
+ frame->can_id = CAN_EFF_FLAG;
+ can_id = ((can_id << 16) |
+ in_be16(®s->rx.idr3_2));
+ can_id = ((can_id & 0xffe00000) |
+ ((can_id & 0x7ffff) << 2)) >> 2;
+ } else {
can_id >>= 4;
frame->can_id = 0;
}
- frame->can_id |= can_id>>1;
- if(can_id & 1)
+ frame->can_id |= can_id >> 1;
+ if (can_id & 1)
frame->can_id |= CAN_RTR_FLAG;
frame->can_dlc = in_8(®s->rx.dlr) & 0xf;
- if( !(frame->can_id & CAN_RTR_FLAG ) ) {
- volatile void __iomem * data = ®s->rx.dsr1_0;
- u16 *payload = (u16 *)frame->data;
- for (i=0; i<(frame->can_dlc+1)/2; i++) {
+ if (!(frame->can_id & CAN_RTR_FLAG)) {
+ void __iomem *data = ®s->rx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
*payload++ = in_be16(data);
- data += 2+_MSCAN_RESERVED_DSR_SIZE;
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
}
}
- dev_dbg(ND2D(ndev), "received pkt: id: %u dlc: %u data: ",
- frame->can_id,frame->can_dlc);
-#ifdef DEBUG
- for(i=0; i<frame->can_dlc && !(frame->can_id & CAN_FLAG_RTR ); i++)
- printk( "%2x ",frame->data[i]);
- printk("\n");
-#endif
-
out_8(®s->canrflg, MSCAN_RXF);
- ndev->last_rx = jiffies;
- can->net_stats.rx_packets++;
- can->net_stats.rx_bytes += frame->can_dlc;
- }
- else if (canrflg & MSCAN_ERR_IF ) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+ dev->last_rx = jiffies;
+#endif
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+ } else if (canrflg & MSCAN_ERR_IF) {
+ dev_dbg(ND2D(dev), "error interrupt (canrflg=%#x)\n",
+ canrflg);
frame->can_id = CAN_ERR_FLAG;
if (canrflg & MSCAN_OVRIF) {
frame->can_id |= CAN_ERR_CRTL;
frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
- can->net_stats.rx_over_errors++;
- }
- else
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ } else
frame->data[1] = 0;
- if ( check_set_state(can, canrflg)) {
- frame->can_id |= CAN_ERR_CRTL;
- switch( can->state ) {
- case CAN_STATE_BUS_WARNING:
- if( (priv->shadow_statflg & MSCAN_RSTAT_MSK) <
+ if (check_set_state(dev, canrflg)) {
+ switch (priv->can.state) {
+ case CAN_STATE_ERROR_WARNING:
+ frame->can_id |= CAN_ERR_CRTL;
+ priv->can.can_stats.error_warning++;
+ if ((priv->shadow_statflg &
+ MSCAN_RSTAT_MSK) <
(canrflg & MSCAN_RSTAT_MSK))
- frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
- if( (priv->shadow_statflg & MSCAN_TSTAT_MSK) <
+ if ((priv->shadow_statflg &
+ MSCAN_TSTAT_MSK) <
(canrflg & MSCAN_TSTAT_MSK))
- frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
break;
- case CAN_STATE_BUS_PASSIVE:
- frame->data[1] |= CAN_ERR_CRTL_PASSIVE;
+ case CAN_STATE_ERROR_PASSIVE:
+ frame->can_id |= CAN_ERR_CRTL;
+ priv->can.can_stats.error_passive++;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
break;
- case CAN_STATE_BUS_OFF:
+ case CAN_STATE_BUS_OFF:
frame->can_id |= CAN_ERR_BUSOFF;
- frame->can_id &= ~CAN_ERR_CRTL;
+ can_bus_off(dev);
+ break;
+ default:
break;
- }
+ }
}
priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
frame->can_dlc = CAN_ERR_DLC;
}
npackets++;
- skb->dev = ndev;
- skb->protocol = __constant_htons(ETH_P_CAN);
- skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_receive_skb(skb);
}
- *budget -= npackets;
- ndev->quota -= npackets;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
+ *budget -= npackets;
+ dev->quota -= npackets;
+#endif
- if ( !(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
- netif_rx_complete(ndev);
- clear_bit(F_RX_PROGRESS, &priv->flags);
- out_8(®s->canrier, in_8(®s->canrier) | MSCAN_ERR_IF | MSCAN_RXFIE);
- ret = 0;
- }
- return ret;
+ if (!(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ napi_complete(&priv->napi);
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ netif_rx_complete(dev, &priv->napi);
+#else
+ netif_rx_complete(dev);
+#endif
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ if (priv->can.state < CAN_STATE_BUS_OFF)
+ out_8(®s->canrier, priv->shadow_canrier);
+ ret = 0;
+ }
+ return ret;
}
-static irqreturn_t
-mscan_isr(int irq, void *dev_id, struct pt_regs *r)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static irqreturn_t mscan_isr(int irq, void *dev_id, struct pt_regs *r)
+#else
+static irqreturn_t mscan_isr(int irq, void *dev_id)
+#endif
{
- struct net_device *ndev = (struct net_device *) dev_id;
- struct can_device *can = ND2CAN(ndev);
- struct mscan_regs *regs = (struct mscan_regs *)(ndev->base_addr);
- struct mscan_priv *priv = can->priv;
- u8 cantflg, canrflg;
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ u8 cantier, cantflg, canrflg;
irqreturn_t ret = IRQ_NONE;
- if ( in_8(®s->cantier) & MSCAN_TXE )
- {
- struct list_head *tmp, *pos;
+ cantier = in_8(®s->cantier) & MSCAN_TXE;
+ cantflg = in_8(®s->cantflg) & cantier;
- cantflg = in_8(®s->cantflg) & MSCAN_TXE;
+ if (cantier && cantflg) {
- list_for_each_safe(pos, tmp, &priv->tx_head) {
- tx_queue_entry_t *entry = list_entry(pos, tx_queue_entry_t, list);
+ struct list_head *tmp, *pos;
+
+ list_for_each_safe(pos, tmp, &priv->tx_head) {
+ struct tx_queue_entry *entry =
+ list_entry(pos, struct tx_queue_entry, list);
u8 mask = entry->mask;
- if( !(cantflg & mask) )
+ if (!(cantflg & mask))
continue;
- if ( in_8(®s->cantaak) & mask ) {
- can->net_stats.tx_dropped++;
- can->net_stats.tx_aborted_errors++;
- }
- else {
- out_8(®s->cantbsel, mask);
- can->net_stats.tx_bytes += in_8(®s->tx.dlr);
- can->net_stats.tx_packets++;
- }
+ out_8(®s->cantbsel, mask);
+ stats->tx_bytes += in_8(®s->tx.dlr);
+ stats->tx_packets++;
+ can_get_echo_skb(dev, entry->id);
priv->tx_active &= ~mask;
list_del(pos);
}
- if ( list_empty(&priv->tx_head) ) {
+ if (list_empty(&priv->tx_head)) {
clear_bit(F_TX_WAIT_ALL, &priv->flags);
clear_bit(F_TX_PROGRESS, &priv->flags);
priv->cur_pri = 0;
- }
- else
- ndev->trans_start = jiffies;
+ } else
+ dev->trans_start = jiffies;
- if( !test_bit(F_TX_WAIT_ALL, &priv->flags) )
- netif_wake_queue(ndev);
+ if (!test_bit(F_TX_WAIT_ALL, &priv->flags))
+ netif_wake_queue(dev);
- out_8( ®s->cantier, priv->tx_active );
+ out_8(®s->cantier, priv->tx_active);
ret = IRQ_HANDLED;
}
- if ( !test_and_set_bit(F_RX_PROGRESS, &priv->flags) &&
- (((canrflg = in_8(®s->canrflg)) & ~MSCAN_STAT_MSK))) {
- if ( check_set_state(can, canrflg) ) {
- out_8(®s->canrflg, MSCAN_CSCIF);
- ret = IRQ_HANDLED;
- }
+ canrflg = in_8(®s->canrflg);
+ if ((canrflg & ~MSCAN_STAT_MSK) &&
+ !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
if (canrflg & ~MSCAN_STAT_MSK) {
priv->shadow_canrier = in_8(®s->canrier);
out_8(®s->canrier, 0);
- netif_rx_schedule(ndev);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ napi_schedule(&priv->napi);
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ netif_rx_schedule(dev, &priv->napi);
+#else
+ netif_rx_schedule(dev);
+#endif
ret = IRQ_HANDLED;
- }
- else
+ } else
clear_bit(F_RX_PROGRESS, &priv->flags);
}
return ret;
}
-static int mscan_do_set_mode(struct can_device *can, can_mode_t mode)
+static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode)
{
+
+ struct mscan_priv *priv = netdev_priv(dev);
+ int ret = 0;
+
+ if (!priv->open_time)
+ return -EINVAL;
+
switch (mode) {
case CAN_MODE_SLEEP:
case CAN_MODE_STOP:
- netif_stop_queue(CAN2ND(can));
- mscan_set_mode(can,
- (mode==CAN_MODE_STOP)? MSCAN_INIT_MODE: MSCAN_SLEEP_MODE);
+ netif_stop_queue(dev);
+ mscan_set_mode(dev,
+ (mode ==
+ CAN_MODE_STOP) ? MSCAN_INIT_MODE :
+ MSCAN_SLEEP_MODE);
break;
case CAN_MODE_START:
- printk("%s: CAN_MODE_START requested\n",__FUNCTION__);
- mscan_set_mode(can, MSCAN_NORMAL_MODE);
- netif_wake_queue(CAN2ND(can));
+ if (priv->can.state <= CAN_STATE_BUS_OFF)
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ ret = mscan_start(dev);
+ if (ret)
+ break;
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
break;
default:
- return -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
+ break;
}
- return 0;
+ return ret;
}
-static
-int mscan_do_set_bit_time(struct can_device *can, struct can_bittime *bt)
+static int mscan_do_set_bittiming(struct net_device *dev)
{
- int ret = 0;
- u8 reg;
- struct mscan_state state;
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
-
- if(bt->type != CAN_BITTIME_STD)
- return -EINVAL;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u8 btr0, btr1;
- spin_lock_irq(&can->irq_lock);
+ btr0 = BTR0_SET_BRP(bt->brp) | BTR0_SET_SJW(bt->sjw);
+ btr1 = (BTR1_SET_TSEG1(bt->prop_seg + bt->phase_seg1) |
+ BTR1_SET_TSEG2(bt->phase_seg2) |
+ BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES));
- mscan_push_state(can, &state);
- ret = mscan_set_mode(can, MSCAN_INIT_MODE);
- if (! ret) {
- reg = BTR0_SET_BRP(bt->std.brp) | BTR0_SET_SJW(bt->std.sjw);
- out_8(®s->canbtr0, reg);
+ dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
- reg = BTR1_SET_TSEG1(bt->std.prop_seg + bt->std.phase_seg1) |
- BTR1_SET_TSEG2(bt->std.phase_seg2) | BTR1_SET_SAM(bt->std.sam);
- out_8(®s->canbtr1, reg);
+ out_8(®s->canbtr0, btr0);
+ out_8(®s->canbtr1, btr1);
- ret = mscan_pop_state(can, &state);
- }
-
- spin_unlock_irq(&can->irq_lock);
- return ret;
+ return 0;
}
-static int mscan_open(struct net_device *ndev)
+static int mscan_open(struct net_device *dev)
{
- int ret = 0;
- struct can_device *can = ND2CAN(ndev);
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
- struct mscan_priv *priv = can->priv;
+ int ret;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
- if((ret = request_irq(ndev->irq, mscan_isr, SA_SHIRQ, ndev->name, ndev)) < 0) {
- printk(KERN_ERR "%s - failed to attach interrupt\n", ndev->name);
+ /* common open */
+ ret = open_candev(dev);
+ if (ret)
return ret;
- }
- INIT_LIST_HEAD(&priv->tx_head);
- /* acceptance mask/acceptance code (accept everything) */
- out_be16(®s->canidar1_0, 0);
- out_be16(®s->canidar3_2, 0);
- out_be16(®s->canidar5_4, 0);
- out_be16(®s->canidar7_6, 0);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ napi_enable(&priv->napi);
+#endif
- out_be16(®s->canidmr1_0, 0xffff);
- out_be16(®s->canidmr3_2, 0xffff);
- out_be16(®s->canidmr5_4, 0xffff);
- out_be16(®s->canidmr7_6, 0xffff);
- /* Two 32 bit Acceptance Filters */
- out_8(®s->canidac, MSCAN_AF_32BIT);
+ ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev);
+ if (ret < 0) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ napi_disable(&priv->napi);
+#endif
+ printk(KERN_ERR "%s - failed to attach interrupt\n",
+ dev->name);
+ return ret;
+ }
- out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_LISTEN);
- mscan_set_mode( can, MSCAN_NORMAL_MODE);
+ priv->open_time = jiffies;
- priv->shadow_statflg = in_8(®s->canrflg) & MSCAN_STAT_MSK;
- priv->cur_pri = 0;
- priv->tx_active = 0;
+ out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_LISTEN);
- out_8(®s->cantier, 0);
- /* Enable receive interrupts. */
- out_8(®s->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
- MSCAN_RSTATE1 | MSCAN_RSTATE0 |
- MSCAN_TSTATE1 | MSCAN_TSTATE0);
+ ret = mscan_start(dev);
+ if (ret)
+ return ret;
- netif_start_queue(ndev);
+ netif_start_queue(dev);
return 0;
}
-static int mscan_close(struct net_device *ndev)
+static int mscan_close(struct net_device *dev)
{
- struct can_device *can = ND2CAN(ndev);
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
- netif_stop_queue(ndev);
+ netif_stop_queue(dev);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ napi_disable(&priv->napi);
+#endif
- /* disable interrupts */
- out_8(®s->cantier, 0);
- out_8(®s->canrier, 0);
- free_irq(ndev->irq, ndev);
+ out_8(®s->cantier, 0);
+ out_8(®s->canrier, 0);
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ close_candev(dev);
+ free_irq(dev->irq, dev);
+ priv->open_time = 0;
- mscan_set_mode( can, MSCAN_INIT_MODE);
- return 0;
+ return 0;
}
-int mscan_register(struct can_device *can, int clock_src)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+static const struct net_device_ops mscan_netdev_ops = {
+ .ndo_open = mscan_open,
+ .ndo_stop = mscan_close,
+ .ndo_start_xmit = mscan_start_xmit,
+};
+#endif
+
+int register_mscandev(struct net_device *dev, int clock_src)
{
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
- u8 ctl1;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 ctl1;
+
+ ctl1 = in_8(®s->canctl1);
+ if (clock_src)
+ ctl1 |= MSCAN_CLKSRC;
+ else
+ ctl1 &= ~MSCAN_CLKSRC;
+
+ ctl1 |= MSCAN_CANE;
+ out_8(®s->canctl1, ctl1);
+ udelay(100);
- ctl1 = in_8(®s->canctl1);
- if(clock_src)
- ctl1 |= MSCAN_CLKSRC;
- else
- ctl1 &= ~MSCAN_CLKSRC;
+ /* acceptance mask/acceptance code (accept everything) */
+ out_be16(®s->canidar1_0, 0);
+ out_be16(®s->canidar3_2, 0);
+ out_be16(®s->canidar5_4, 0);
+ out_be16(®s->canidar7_6, 0);
- ctl1 |= MSCAN_CANE;
- out_8(®s->canctl1, ctl1);
- udelay(100);
+ out_be16(®s->canidmr1_0, 0xffff);
+ out_be16(®s->canidmr3_2, 0xffff);
+ out_be16(®s->canidmr5_4, 0xffff);
+ out_be16(®s->canidmr7_6, 0xffff);
+ /* Two 32 bit Acceptance Filters */
+ out_8(®s->canidac, MSCAN_AF_32BIT);
- mscan_set_mode( can, MSCAN_INIT_MODE );
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
- return register_netdev(CAN2ND(can));
+ return register_candev(dev);
}
-EXPORT_SYMBOL(mscan_register);
+EXPORT_SYMBOL_GPL(register_mscandev);
-void mscan_unregister(struct can_device *can)
+void unregister_mscandev(struct net_device *dev)
{
- struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
- mscan_set_mode( can, MSCAN_INIT_MODE );
- out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_CANE);
- unregister_netdev(CAN2ND(can));
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_CANE);
+ unregister_candev(dev);
}
+EXPORT_SYMBOL_GPL(unregister_mscandev);
-EXPORT_SYMBOL(mscan_unregister);
-
-struct can_device *alloc_mscandev()
+struct net_device *alloc_mscandev(void)
{
- struct can_device *can;
- struct net_device *ndev;
- struct mscan_priv *priv;
- int i;
+ struct net_device *dev;
+ struct mscan_priv *priv;
+ int i;
- can = alloc_candev(sizeof(struct mscan_priv));
- if(!can)
+ dev = alloc_candev(sizeof(struct mscan_priv), MSCAN_ECHO_SKB_MAX);
+ if (!dev)
return NULL;
- ndev = CAN2ND(can);
- priv = can->priv;
+ priv = netdev_priv(dev);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ dev->netdev_ops = &mscan_netdev_ops;
+#else
+ dev->open = mscan_open;
+ dev->stop = mscan_close;
+ dev->hard_start_xmit = mscan_start_xmit;
+#endif
- ndev->watchdog_timeo = MSCAN_WATCHDOG_TIMEOUT;
- ndev->open = mscan_open;
- ndev->stop = mscan_close;
- ndev->hard_start_xmit = mscan_hard_start_xmit;
- ndev->tx_timeout = mscan_tx_timeout;
+ dev->flags |= IFF_ECHO; /* we support local echo */
- ndev->poll = mscan_rx_poll;
- ndev->weight = 8;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ priv->dev = dev;
+ netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
+#else
+ dev->poll = mscan_rx_poll;
+ dev->weight = 8;
+#endif
- can->do_set_bit_time = mscan_do_set_bit_time;
- can->do_set_mode = mscan_do_set_mode;
+ priv->can.bittiming_const = &mscan_bittiming_const;
+ priv->can.do_set_bittiming = mscan_do_set_bittiming;
+ priv->can.do_set_mode = mscan_do_set_mode;
- for(i=0; i< TX_QUEUE_SIZE; i++)
- priv->tx_queue[i].mask = 1<<i;
+ for (i = 0; i < TX_QUEUE_SIZE; i++) {
+ priv->tx_queue[i].id = i;
+ priv->tx_queue[i].mask = 1 << i;
+ }
- return can;
+ return dev;
}
-
-EXPORT_SYMBOL(alloc_mscandev);
+EXPORT_SYMBOL_GPL(alloc_mscandev);
MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("CAN port driver for a mscan based chips");
+MODULE_DESCRIPTION("CAN port driver for a MSCAN based chips");