/*
- * mscan.c
+ * CAN bus driver for the alone generic (as possible as) MSCAN controller.
*
- * DESCRIPTION:
- * CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
*
- * AUTHOR:
- * Andrey Volkov <avolkov@varma-el.com>
+ * 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
*
- * 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/kernel.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/list.h>
-#include <linux/can.h>
-#include <linux/can/dev.h>
-#include <linux/can/error.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 "mscan.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
#define MSCAN_NORMAL_MODE 0
#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ)
#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ)
#define MSCAN_SET_MODE_RETRIES 255
-
+#define MSCAN_ECHO_SKB_MAX 3
#define BTR0_BRP_MASK 0x3f
#define BTR0_SJW_SHIFT 6
#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 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,
struct mscan_priv {
struct can_priv can;
long open_time;
- volatile unsigned long flags;
+ unsigned long flags;
u8 shadow_statflg;
u8 shadow_canrier;
u8 cur_pri;
+ u8 prev_buf_id;
u8 tx_active;
struct list_head tx_head;
struct tx_queue_entry tx_queue[TX_QUEUE_SIZE];
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+#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_TX_WAIT_ALL 2
static enum can_state state_map[] = {
- CAN_STATE_ACTIVE,
- CAN_STATE_BUS_WARNING,
- CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_ERROR_ACTIVE,
+ CAN_STATE_ERROR_WARNING,
+ CAN_STATE_ERROR_PASSIVE,
CAN_STATE_BUS_OFF
};
if (mode != MSCAN_NORMAL_MODE) {
if (priv->tx_active) {
- /* Abort transfers before going to sleep */
- out_8(®s->cantier, 0);
+ /* Abort transfers before going to sleep */#
out_8(®s->cantarq, priv->tx_active);
- out_8(®s->cantier, priv->tx_active);
+ /* Suppress TX done interrupts */
+ out_8(®s->cantier, 0);
}
canctl1 = in_8(®s->canctl1);
if (i >= MSCAN_SET_MODE_RETRIES)
ret = -ENODEV;
else
- priv->can.state = CAN_STATE_ACTIVE;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
}
}
return ret;
out_8(®s->canrier, 0);
INIT_LIST_HEAD(&priv->tx_head);
+ priv->prev_buf_id = 0;
priv->cur_pri = 0;
priv->tx_active = 0;
priv->shadow_canrier = 0;
return 0;
}
-static int mscan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+#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 mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
if (frame->can_dlc > 8)
return -EINVAL;
- dev_dbg(ND2D(dev), "%s\n", __func__);
out_8(®s->cantier, 0);
i = ~priv->tx_active & MSCAN_TXE;
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)
- set_bit(F_TX_WAIT_ALL, &priv->flags);
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(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(dev), "sending extended frame\n");
-
can_id = (frame->can_id & CAN_EFF_MASK) << 1;
if (rtr)
can_id |= 1;
can_id >>= 16;
can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | (3 << 3);
} else {
- dev_dbg(ND2D(dev), "sending standard frame\n");
can_id = (frame->can_id & CAN_SFF_MASK) << 5;
if (rtr)
can_id |= 1 << 4;
out_be16(®s->tx.idr1_0, can_id);
if (!rtr) {
- volatile void __iomem *data = ®s->tx.dsr1_0;
+ 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++) {
MSCAN_STATE_TX(canrflg))];
if (priv->can.state < state)
ret = 1;
- if (state == CAN_STATE_BUS_OFF)
- can_bus_off(dev);
priv->can.state = state;
return ret;
}
static int mscan_rx_poll(struct net_device *dev, int *budget)
#endif
{
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+#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
int quota = min(dev->quota, *budget);
#endif
struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
- struct net_device_stats *stats = dev->get_stats(dev);
+#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;
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(dev), "packet dropped\n");
continue;
}
- frame = (struct can_frame *)skb_put(skb, sizeof(*frame));
- memset(frame, 0, sizeof(*frame));
-
if (canrflg & MSCAN_RXF) {
can_id = in_be16(®s->rx.idr1_0);
if (can_id & (1 << 3)) {
frame->can_dlc = in_8(®s->rx.dlr) & 0xf;
if (!(frame->can_id & CAN_RTR_FLAG)) {
- volatile void __iomem *data = ®s->rx.dsr1_0;
+ 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);
}
}
- dev_dbg(ND2D(dev),
- "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_RTR_FLAG); i++)
- printk(KERN_DEBUG "%2x ", frame->data[i]);
- printk(KERN_DEBUG "\n");
-#endif
-
out_8(®s->canrflg, MSCAN_RXF);
+#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;
stats->rx_over_errors++;
+ stats->rx_errors++;
} else
frame->data[1] = 0;
if (check_set_state(dev, canrflg)) {
- frame->can_id |= CAN_ERR_CRTL;
switch (priv->can.state) {
- case CAN_STATE_BUS_WARNING:
+ 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;
+ CAN_ERR_CRTL_RX_WARNING;
if ((priv->shadow_statflg &
MSCAN_TSTAT_MSK) <
frame->data[1] |=
CAN_ERR_CRTL_TX_WARNING;
break;
- case CAN_STATE_BUS_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;
+ CAN_ERR_CRTL_RX_PASSIVE;
break;
case CAN_STATE_BUS_OFF:
frame->can_id |= CAN_ERR_BUSOFF;
- frame->can_id &= ~CAN_ERR_CRTL;
+ can_bus_off(dev);
break;
default:
break;
}
npackets++;
- skb->dev = dev;
- skb->protocol = __constant_htons(ETH_P_CAN);
- skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_receive_skb(skb);
}
#endif
if (!(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+#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 0
- out_8(®s->canrier,
- in_8(®s->canrier) | MSCAN_ERR_IF | MSCAN_RXFIE);
-#else
if (priv->can.state < CAN_STATE_BUS_OFF)
out_8(®s->canrier, priv->shadow_canrier);
-#endif
ret = 0;
}
return ret;
}
-
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
static irqreturn_t mscan_isr(int irq, void *dev_id, struct pt_regs *r)
#else
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;
- struct net_device_stats *stats = dev->get_stats(dev);
+#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;
canrflg = in_8(®s->canrflg);
if ((canrflg & ~MSCAN_STAT_MSK) &&
!test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
-#if 0
- printk(KERN_DEBUG "%s: canrflg=%#02x canrier=%#02x\n",
- dev->name, canrflg, in_8(®s->canrier));
-#endif
-#if 0
- if (check_set_state(dev, canrflg)) {
- out_8(®s->canrflg, MSCAN_CSCIF);
- ret = IRQ_HANDLED;
- }
-#endif
if (canrflg & ~MSCAN_STAT_MSK) {
priv->shadow_canrier = in_8(®s->canrier);
out_8(®s->canrier, 0);
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+#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);
BTR1_SET_TSEG2(bt->phase_seg2) |
BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES));
- dev_info(ND2D(dev), "BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+ dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
out_8(®s->canbtr0, btr0);
out_8(®s->canbtr1, btr1);
struct mscan_priv *priv = netdev_priv(dev);
struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
- /* determine and set bittime */
- ret = can_set_bittiming(dev);
+ /* common open */
+ ret = open_candev(dev);
if (ret)
return ret;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
napi_enable(&priv->napi);
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
- ret = request_irq(dev->irq, mscan_isr, SA_SHIRQ, dev->name, dev);
-#else
- ret = request_irq(dev->irq, mscan_isr, IRQF_SHARED, dev->name, dev);
-#endif
+ 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);
struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
struct mscan_priv *priv = netdev_priv(dev);
+ netif_stop_queue(dev);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
napi_disable(&priv->napi);
#endif
out_8(®s->cantier, 0);
out_8(®s->canrier, 0);
- free_irq(dev->irq, dev);
mscan_set_mode(dev, MSCAN_INIT_MODE);
- can_close_cleanup(dev);
- netif_stop_queue(dev);
+ close_candev(dev);
+ free_irq(dev->irq, dev);
priv->open_time = 0;
return 0;
}
+#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 *)dev->base_addr;
mscan_set_mode(dev, MSCAN_INIT_MODE);
- return register_netdev(dev);
+ return register_candev(dev);
}
-EXPORT_SYMBOL(register_mscandev);
+EXPORT_SYMBOL_GPL(register_mscandev);
void unregister_mscandev(struct net_device *dev)
{
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_netdev(dev);
+ unregister_candev(dev);
}
-EXPORT_SYMBOL(unregister_mscandev);
+EXPORT_SYMBOL_GPL(unregister_mscandev);
struct net_device *alloc_mscandev(void)
{
struct mscan_priv *priv;
int i;
- dev = alloc_candev(sizeof(struct mscan_priv));
+ dev = alloc_candev(sizeof(struct mscan_priv), MSCAN_ECHO_SKB_MAX);
if (!dev)
return NULL;
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_hard_start_xmit;
+ dev->hard_start_xmit = mscan_start_xmit;
+#endif
dev->flags |= IFF_ECHO; /* we support local echo */
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+#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
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");