]> rtime.felk.cvut.cz Git - socketcan-devel.git/commitdiff
Added drivers of MSCAN based mpc52xx onchip CAN
authoravolkov <avolkov@030b6a49-0b11-0410-94ab-b0dab22257f2>
Thu, 15 Jun 2006 13:12:14 +0000 (13:12 +0000)
committeravolkov <avolkov@030b6a49-0b11-0410-94ab-b0dab22257f2>
Thu, 15 Jun 2006 13:12:14 +0000 (13:12 +0000)
git-svn-id: svn://svn.berlios.de//socketcan/trunk@32 030b6a49-0b11-0410-94ab-b0dab22257f2

kernel/2.6/drivers/net/can/Kconfig
kernel/2.6/drivers/net/can/mscan/Makefile [new file with mode: 0644]
kernel/2.6/drivers/net/can/mscan/mpc52xx_can.c [new file with mode: 0644]
kernel/2.6/drivers/net/can/mscan/mscan.c [new file with mode: 0644]
kernel/2.6/drivers/net/can/mscan/mscan.h [new file with mode: 0644]

index 520c486ae60f57d36267ba8636ec5a95f396dd8d..1911306b4d8cc716a9e85a8e87466c26f197fb27 100644 (file)
@@ -30,7 +30,7 @@ config CAN_SJA1000
          which handles how to access your chip registers.
 
 config CAN_MSCAN
-       depends on CAN
+       depends on CAN && (PPC || M68K || M68KNOMMU)
        tristate "Support for a Freescale MSCAN based chips"
        ---help---
         The Motorola Scalable Controller Area Network (MSCAN) definition
@@ -40,7 +40,7 @@ config CAN_MSCAN
 
 config CAN_MPC52XX
        tristate "Freescale MPC5200 onboard CAN controller"
-       depends on CAN_MSCAN && PPC_MPC52xx
+       depends on CAN_MSCAN && (PPC_MPC52xx || PPC_52xx)
        default LITE5200
        ---help---
         If you say yes here you get support for Freescale MPC5200
diff --git a/kernel/2.6/drivers/net/can/mscan/Makefile b/kernel/2.6/drivers/net/can/mscan/Makefile
new file mode 100644 (file)
index 0000000..f0a2480
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the MSCAN based device drivers.
+#
+#
+
+obj-$(CONFIG_CAN_MSCAN)                += mscan.o
+obj-$(CONFIG_CAN_MPC52XX)      += mpc52xx_can.o
diff --git a/kernel/2.6/drivers/net/can/mscan/mpc52xx_can.c b/kernel/2.6/drivers/net/can/mscan/mpc52xx_can.c
new file mode 100644 (file)
index 0000000..7e6efec
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * $Id:$
+ *
+ * DESCRIPTION:
+ *  CAN bus driver for the Freescale MPC52xx embedded CPU.
+ *
+ * AUTHOR:
+ *  Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ *  2004-2005, 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
+ *
+ * HISTORY:
+ *      2005-02-03 created
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can/can.h>
+#include <can/can_device.h>
+#include <can/mscan/mscan.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+
+static int __devinit mpc52xx_can_probe(struct platform_device *pdev)
+{
+       struct can_device *can;
+       struct resource *mem;
+       struct net_device *ndev;
+       struct mscan_platform_data *pdata = pdev->dev.platform_data;
+       u32 mem_size;
+       int ret = -ENODEV;
+
+       if(!pdata)
+               return ret;
+
+       can = alloc_mscandev();
+       if (!can)
+               return -ENOMEM;
+
+       ndev = CAN2ND(can);
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ndev->irq = platform_get_irq(pdev, 0);
+       if (!mem || !ndev->irq)
+               goto req_error;
+
+       mem_size = mem->end - mem->start + 1;
+       if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
+               dev_err(&pdev->dev, "resource unavailable\n");
+               goto req_error;
+       }
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+       SET_MODULE_OWNER(THIS_MODULE);
+
+       ndev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+
+       if (!ndev->base_addr) {
+               dev_err(&pdev->dev, "failed to map can port\n");
+               ret = -ENOMEM;
+               goto fail_map;
+       }
+
+       can->can_sys_clock = pdata->clock_frq;
+
+       platform_set_drvdata(pdev, can);
+
+       ret = mscan_register(can, pdata->clock_src);
+       if (ret >= 0) {
+               dev_info(&pdev->dev, "probe for a port 0x%lX done\n", ndev->base_addr);
+               return ret;
+       }
+
+       iounmap((unsigned long *)ndev->base_addr);
+fail_map:
+       release_mem_region(mem->start, mem_size);
+req_error:
+       free_candev(can);
+       dev_err(&pdev->dev, "probe failed\n");
+       return ret;
+}
+
+static int __devexit mpc52xx_can_remove(struct platform_device *pdev)
+{
+       struct can_device *can = platform_get_drvdata(pdev);
+       struct resource *mem;
+
+       platform_set_drvdata(pdev, NULL);
+       mscan_unregister(can);
+
+       iounmap((volatile void __iomem *)(CAN2ND(can)->base_addr));
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(mem->start, mem->start - mem->end + 1);
+       free_candev(can);
+       return 0;
+}
+
+static struct platform_driver mpc52xx_can_driver = {
+       .driver         = {
+               .name     = "mpc52xx-mscan",
+       },
+       .probe    = mpc52xx_can_probe,
+       .remove   = __devexit_p(mpc52xx_can_remove)
+#ifdef CONFIG_PM
+/*     .suspend        = mpc52xx_can_suspend,  TODO */
+/*     .resume         = mpc52xx_can_resume,   TODO */
+#endif
+};
+
+int __init mpc52xx_can_init(void)
+{
+       printk(KERN_INFO "%s initializing\n", mpc52xx_can_driver.driver.name);
+       return platform_driver_register(&mpc52xx_can_driver);
+}
+
+void __exit mpc52xx_can_exit(void)
+{
+       platform_driver_unregister(&mpc52xx_can_driver);
+       printk(KERN_INFO "%s unloaded\n", mpc52xx_can_driver.driver.name);
+}
+
+
+module_init(mpc52xx_can_init);
+module_exit(mpc52xx_can_exit);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
+MODULE_LICENSE("GPLv2");
diff --git a/kernel/2.6/drivers/net/can/mscan/mscan.c b/kernel/2.6/drivers/net/can/mscan/mscan.c
new file mode 100644 (file)
index 0000000..a4dfe7f
--- /dev/null
@@ -0,0 +1,683 @@
+/*
+ * $Id:$
+ *
+ * mscan.c
+ *
+ * DESCRIPTION:
+ *  CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ *
+ * 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
+ *
+ */
+
+#include <linux/config.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/list.h>
+#include <asm/io.h>
+
+#include <can/can_device.h>
+#include <can/mscan/mscan.h>
+
+#define MSCAN_NORMAL_MODE      0
+#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)
+
+struct mscan_state {
+       u8 mode;
+       u8 canrier;
+       u8 cantier;
+};
+
+#define TX_QUEUE_SIZE  3
+
+typedef struct {
+       struct list_head list;
+       u8 mask;
+} tx_queue_entry_t;
+
+struct mscan_priv {
+       volatile unsigned long flags;
+       u8 shadow_statflg;
+       u8 shadow_canrier;
+       u8 cur_pri;
+       u8 tx_active;
+
+       struct list_head tx_head;
+       tx_queue_entry_t tx_queue[TX_QUEUE_SIZE];
+};
+
+#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)
+{
+       struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
+       int ret = 0;
+       int i;
+       u8 canctl1;
+
+       if( mode != MSCAN_NORMAL_MODE ) {
+               canctl1 = in_8(&regs->canctl1);
+               if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
+                       out_8( &regs->canctl0, in_8(&regs->canctl0) | MSCAN_SLPRQ );
+                       for (i = 0; i < 255; i++ ) {
+                               if ( in_8(&regs->canctl1) & MSCAN_SLPAK )
+                                       break;
+                               udelay(100);
+                       }
+                       if(i>255)
+                          ret=-ENODEV;
+               }
+
+               if ( !ret && (mode & MSCAN_INITRQ) && (canctl1 & MSCAN_INITAK) == 0) {
+                       out_8( &regs->canctl0, in_8( &regs->canctl0 ) | MSCAN_INITRQ);
+                       for (i = 0; i < 255; i++ ) {
+                               if ( in_8(&regs->canctl1) & MSCAN_INITAK )
+                                       break;
+                       }
+                       if(i>255)
+                          ret=-ENODEV;
+               }
+
+               if ( !ret && (mode & MSCAN_CSWAI) )
+                       out_8( &regs->canctl0, in_8(&regs->canctl0) | MSCAN_CSWAI);
+
+       } else  {
+               canctl1 = in_8(&regs->canctl1);
+               if ( canctl1 & (MSCAN_SLPAK | MSCAN_INITAK) ) {
+                out_8(&regs->canctl0, in_8(&regs->canctl0) &
+                                         ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+                 for (i = 0; i < 255; i++ ) {
+                       canctl1 = in_8(&regs->canctl1);
+                   if ( (canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)) ==0 )
+                               break;
+                 }
+                 if(i>255)
+                   ret=-ENODEV;
+               }
+       }
+  return ret;
+}
+
+static void mscan_push_state(struct can_device *can, struct mscan_state *state)
+{
+       struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
+
+       state->mode = in_8(&regs->canctl0) &
+                                       (MSCAN_SLPRQ | MSCAN_INITRQ | MSCAN_CSWAI);
+       state->canrier = in_8(&regs->canrier);
+       state->cantier = in_8(&regs->cantier);
+}
+
+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( &regs->canrier, state->canrier);
+               out_8( &regs->cantier, state->cantier);
+       }
+       return ret;
+}
+
+static int mscan_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       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;
+       int i, rtr, buf_id;
+       u32 can_id;
+
+       if ( frame->can_dlc > 8 )
+               return -EINVAL;
+
+       dev_dbg( ND2D(ndev), "%s\n", __FUNCTION__);
+       out_8(&regs->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)
+                               set_bit(F_TX_WAIT_ALL, &priv->flags);
+                       netif_stop_queue(ndev);
+               case 2:
+                       set_bit(F_TX_PROGRESS, &priv->flags);
+       }
+       out_8(&regs->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 )
+                       can_id |= 1;
+               out_be16(&regs->tx.idr3_2, can_id);
+
+               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;
+       }
+       out_be16(&regs->tx.idr1_0, can_id);
+
+       if ( !rtr ) {
+               volatile void __iomem *data = &regs->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;
+               }
+       }
+
+       out_8(&regs->tx.dlr, frame->can_dlc);
+       out_8(&regs->tx.tbpr, priv->cur_pri);
+
+       /* Start transmission. */
+       out_8(&regs->cantflg, 1<<buf_id);
+
+       if ( ! test_bit(F_TX_PROGRESS, &priv->flags) )
+               ndev->trans_start = jiffies;
+
+       list_add_tail( &priv->tx_queue[buf_id].list, &priv->tx_head);
+
+       dev_kfree_skb (skb);
+
+       /* Enable interrupt. */
+       priv->tx_active |= 1<<buf_id;
+       out_8( &regs->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(&regs->cantier, 0);
+
+       mask = list_entry( priv->tx_head.next, tx_queue_entry_t, list)->mask;
+       ndev->trans_start = jiffies;
+       out_8(&regs->cantarq, mask);
+       out_8(&regs->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);
+
+}
+
+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)
+{
+ can_state_t state;
+ int ret = 0;
+
+ if ( !(canrflg & MSCAN_CSCIF) || 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;
+}
+
+static int mscan_rx_poll(struct net_device *ndev, int *budget)
+{
+       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);
+       int ret = 1;
+       struct sk_buff *skb;
+       struct can_frame *frame;
+       u32 can_id;
+       u8  canrflg;
+       int i;
+
+       while ( npackets < quota &&
+       ( (canrflg = in_8(&regs->canrflg)) & (MSCAN_RXF | MSCAN_ERR_IF) )) {
+
+               skb = dev_alloc_skb(sizeof(struct can_frame));
+               if (!skb) {
+                       if(printk_ratelimit())
+                               dev_notice(ND2D(ndev), "packet dropped\n");
+                       can->net_stats.rx_dropped++;
+                       out_8(&regs->canrflg, canrflg);
+                       continue;
+               }
+
+               frame = (struct can_frame *)skb_put(skb,sizeof(struct can_frame));
+
+               if (canrflg & MSCAN_RXF) {
+                       can_id = in_be16( &regs->rx.idr1_0 );
+                       if (can_id & (1<<3) ) {
+                          frame->can_id = CAN_EFF_FLAG;
+                          can_id = (can_id << 16) | in_be16(&regs->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_RTR_FLAG;
+                       frame->can_dlc = in_8(&regs->rx.dlr) & 0xf;
+
+                       if( !(frame->can_id & CAN_RTR_FLAG ) ) {
+                               volatile void __iomem * data = &regs->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;
+                               }
+                       }
+
+                       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->payload.data[i]);
+                       printk("\n");
+               #endif
+
+                       out_8(&regs->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 ) {
+                       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
+                               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) <
+                                           (canrflg & MSCAN_RSTAT_MSK))
+                                               frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+
+                                       if( (priv->shadow_statflg & MSCAN_TSTAT_MSK) <
+                                           (canrflg & MSCAN_TSTAT_MSK))
+                                               frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+                                       break;
+                                 case CAN_STATE_BUS_PASSIVE:
+                                       frame->data[1] |= CAN_ERR_CRTL_PASSIVE;
+                                       break;
+                                 case CAN_STATE_BUS_OFF:
+                                       frame->can_id |= CAN_ERR_BUSOFF;
+                                       frame->can_id &= ~CAN_ERR_CRTL;
+                                       break;
+                         }
+                       }
+                       priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+                       frame->can_dlc = CAN_ERR_DLC;
+                       out_8(&regs->canrflg, MSCAN_ERR_IF);
+               }
+
+               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 ( !(in_8(&regs->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
+       netif_rx_complete(ndev);
+       clear_bit(F_RX_PROGRESS, &priv->flags);
+       out_8(&regs->canrier, in_8(&regs->canrier) | MSCAN_ERR_IF | MSCAN_RXFIE);
+       ret = 0;
+  }
+  return ret;
+}
+
+static irqreturn_t
+mscan_isr(int irq, void *dev_id, struct pt_regs *r)
+{
+       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;
+       irqreturn_t ret = IRQ_NONE;
+
+       if ( in_8(&regs->cantier) & MSCAN_TXE )
+       {
+               struct list_head *tmp, *pos;
+
+               cantflg = in_8(&regs->cantflg) & MSCAN_TXE;
+
+               list_for_each_safe(pos, tmp, &priv->tx_head)  {
+                       tx_queue_entry_t *entry = list_entry(pos, tx_queue_entry_t, list);
+                       u8 mask = entry->mask;
+
+                       if( !(cantflg & mask) )
+                               continue;
+
+                       if ( in_8(&regs->cantaak) & mask ) {
+                               can->net_stats.tx_dropped++;
+                               can->net_stats.tx_aborted_errors++;
+                       }
+                       else {
+                               out_8(&regs->cantbsel, mask);
+                               can->net_stats.tx_bytes += in_8(&regs->tx.dlr);
+                               can->net_stats.tx_packets++;
+                       }
+                       priv->tx_active &= ~mask;
+                       list_del(pos);
+               }
+
+               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;
+
+               if( !test_bit(F_TX_WAIT_ALL, &priv->flags) )
+                       netif_wake_queue(ndev);
+
+               out_8( &regs->cantier, priv->tx_active );
+               ret = IRQ_HANDLED;
+       }
+
+       if ( !test_and_set_bit(F_RX_PROGRESS, &priv->flags) &&
+               (((canrflg = in_8(&regs->canrflg)) & ~MSCAN_STAT_MSK))) {
+               if ( check_set_state(can, canrflg) ) {
+                       out_8(&regs->canrflg, MSCAN_CSCIF);
+                       ret = IRQ_HANDLED;
+               }
+               if (canrflg & ~MSCAN_STAT_MSK) {
+                       priv->shadow_canrier = in_8(&regs->canrier);
+                       out_8(&regs->canrier, 0);
+                       netif_rx_schedule(ndev);
+                       ret = IRQ_HANDLED;
+               }
+               else
+                       clear_bit(F_RX_PROGRESS, &priv->flags);
+       }
+       return ret;
+}
+
+static int mscan_do_set_mode(struct can_device *can, can_mode_t mode)
+{
+       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);
+               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));
+               break;
+
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static
+int mscan_do_set_bit_time(struct can_device *can, struct can_bittime *bt)
+{
+       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;
+
+       spin_lock_irq(&can->irq_lock);
+
+       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(&regs->canbtr0, reg);
+
+               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(&regs->canbtr1, reg);
+
+           ret = mscan_pop_state(can, &state);
+    }
+
+       spin_unlock_irq(&can->irq_lock);
+       return ret;
+}
+
+static int mscan_open(struct net_device *ndev)
+{
+  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;
+
+  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);
+       return ret;
+  }
+
+  INIT_LIST_HEAD(&priv->tx_head);
+  /* acceptance mask/acceptance code (accept everything) */
+  out_be16(&regs->canidar1_0, 0);
+  out_be16(&regs->canidar3_2, 0);
+  out_be16(&regs->canidar5_4, 0);
+  out_be16(&regs->canidar7_6, 0);
+
+  out_be16(&regs->canidmr1_0, 0xffff);
+  out_be16(&regs->canidmr3_2, 0xffff);
+  out_be16(&regs->canidmr5_4, 0xffff);
+  out_be16(&regs->canidmr7_6, 0xffff);
+  /* Two 32 bit Acceptance Filters */
+  out_8(&regs->canidac, MSCAN_AF_32BIT);
+
+  out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_LISTEN);
+  mscan_set_mode( can, MSCAN_NORMAL_MODE);
+
+  priv->shadow_statflg = in_8(&regs->canrflg) & MSCAN_STAT_MSK;
+  priv->cur_pri = 0;
+  priv->tx_active = 0;
+
+  out_8(&regs->cantier, 0);
+  /* Enable receive interrupts. */
+  out_8(&regs->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
+                                               MSCAN_RSTATE1 | MSCAN_RSTATE0 |
+                                               MSCAN_TSTATE1 | MSCAN_TSTATE0);
+
+  netif_start_queue(ndev);
+
+ return 0;
+}
+
+static int mscan_close(struct net_device *ndev)
+{
+  struct can_device *can = ND2CAN(ndev);
+  struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
+
+  netif_stop_queue(ndev);
+
+  /* disable interrupts */
+  out_8(&regs->cantier, 0);
+  out_8(&regs->canrier, 0);
+  free_irq(ndev->irq, ndev);
+
+  mscan_set_mode( can, MSCAN_INIT_MODE);
+  return 0;
+}
+
+int mscan_register(struct can_device *can, int clock_src)
+{
+  struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
+  u8 ctl1;
+
+  ctl1 = in_8(&regs->canctl1);
+  if(clock_src)
+       ctl1 |= MSCAN_CLKSRC;
+  else
+       ctl1 &= ~MSCAN_CLKSRC;
+
+  ctl1 |= MSCAN_CANE;
+  out_8(&regs->canctl1, ctl1);
+  udelay(100);
+
+  mscan_set_mode( can, MSCAN_INIT_MODE );
+
+  return register_netdev(CAN2ND(can));
+}
+EXPORT_SYMBOL(mscan_register);
+
+void mscan_unregister(struct can_device *can)
+{
+  struct mscan_regs *regs = (struct mscan_regs *)(CAN2ND(can)->base_addr);
+  mscan_set_mode( can, MSCAN_INIT_MODE );
+  out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_CANE);
+  unregister_netdev(CAN2ND(can));
+}
+
+EXPORT_SYMBOL(mscan_unregister);
+
+struct can_device *alloc_mscandev()
+{
+  struct can_device *can;
+  struct net_device *ndev;
+  struct mscan_priv *priv;
+  int i;
+
+  can = alloc_candev(sizeof(struct mscan_priv));
+  if(!can)
+               return NULL;
+  ndev = CAN2ND(can);
+  priv = can->priv;
+
+  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;
+
+  ndev->poll                   = mscan_rx_poll;
+  ndev->weight                 = 8;
+
+  can->do_set_bit_time = mscan_do_set_bit_time;
+  can->do_set_mode             = mscan_do_set_mode;
+
+  for(i=0; i< TX_QUEUE_SIZE; i++)
+       priv->tx_queue[i].mask = 1<<i;
+
+  return can;
+}
+
+EXPORT_SYMBOL(alloc_mscandev);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for a mscan based chips");
diff --git a/kernel/2.6/drivers/net/can/mscan/mscan.h b/kernel/2.6/drivers/net/can/mscan/mscan.h
new file mode 100644 (file)
index 0000000..157e367
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * $Id:$
+ *
+ * DESCRIPTION:
+ *  Definitions of consts/structs to drive the Freescale MSCAN.
+ *
+ * AUTHOR:
+ *  Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ *  2004-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
+ *
+ */
+
+#ifndef __MSCAN_H__
+#define __MSCAN_H__
+
+#include <linux/config.h>
+#include <asm/types.h>
+
+/* MSCAN control register 0 (CANCTL0) bits */
+#define MSCAN_RXFRM            0x80
+#define MSCAN_RXACT            0x40
+#define MSCAN_CSWAI            0x20
+#define MSCAN_SYNCH            0x10
+#define MSCAN_TIME             0x08
+#define MSCAN_WUPE             0x04
+#define MSCAN_SLPRQ            0x02
+#define MSCAN_INITRQ   0x01
+
+/* MSCAN control register 1 (CANCTL1) bits */
+#define MSCAN_CANE             0x80
+#define MSCAN_CLKSRC   0x40
+#define MSCAN_LOOPB            0x20
+#define MSCAN_LISTEN   0x10
+#define MSCAN_WUPM             0x04
+#define MSCAN_SLPAK            0x02
+#define MSCAN_INITAK   0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+#define MSCAN_CLKSRC_BUS       0
+#define MSCAN_CLKSRC_XTAL      MSCAN_CLKSRC
+#else
+#define MSCAN_CLKSRC_BUS       MSCAN_CLKSRC
+#define MSCAN_CLKSRC_XTAL      0
+#endif
+
+/* MSCAN receiver flag register (CANRFLG) bits */
+#define MSCAN_WUPIF            0x80
+#define MSCAN_CSCIF            0x40
+#define MSCAN_RSTAT1   0x20
+#define MSCAN_RSTAT0   0x10
+#define MSCAN_TSTAT1   0x08
+#define MSCAN_TSTAT0   0x04
+#define MSCAN_OVRIF            0x02
+#define MSCAN_RXF              0x01
+#define MSCAN_ERR_IF   (MSCAN_OVRIF | MSCAN_CSCIF)
+#define MSCAN_RSTAT_MSK        (MSCAN_RSTAT1 | MSCAN_RSTAT0)
+#define MSCAN_TSTAT_MSK        (MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK)
+
+#define MSCAN_STATE_BUS_OFF \
+                                       (MSCAN_RSTAT1 | MSCAN_RSTAT0 | MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STATE_TX(canrflg)  (((canrflg)&MSCAN_TSTAT_MSK)>>2)
+#define MSCAN_STATE_RX(canrflg)  (((canrflg)&MSCAN_RSTAT_MSK)>>4)
+#define MSCAN_STATE_ACTIVE             0
+#define MSCAN_STATE_WARNING            1
+#define MSCAN_STATE_PASSIVE            2
+#define MSCAN_STATE_BUSOFF             3
+
+
+/* MSCAN receiver interrupt enable register (CANRIER) bits */
+#define MSCAN_WUPIE            0x80
+#define MSCAN_CSCIE            0x40
+#define MSCAN_RSTATE1  0x20
+#define MSCAN_RSTATE0  0x10
+#define MSCAN_TSTATE1  0x08
+#define MSCAN_TSTATE0  0x04
+#define MSCAN_OVRIE            0x02
+#define MSCAN_RXFIE            0x01
+
+/* MSCAN transmitter flag register (CANTFLG) bits */
+#define MSCAN_TXE2             0x04
+#define MSCAN_TXE1             0x02
+#define MSCAN_TXE0             0x01
+#define MSCAN_TXE              (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0)
+
+/* MSCAN transmitter interrupt enable register (CANTIER) bits */
+#define MSCAN_TXIE2            0x04
+#define MSCAN_TXIE1            0x02
+#define MSCAN_TXIE0            0x01
+#define MSCAN_TXIE             (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0)
+
+/* MSCAN transmitter message abort request (CANTARQ) bits */
+#define MSCAN_ABTRQ2   0x04
+#define MSCAN_ABTRQ1   0x02
+#define MSCAN_ABTRQ0   0x01
+
+/* MSCAN transmitter message abort ack (CANTAAK) bits */
+#define MSCAN_ABTAK2   0x04
+#define MSCAN_ABTAK1   0x02
+#define MSCAN_ABTAK0   0x01
+
+/* MSCAN transmit buffer selection (CANTBSEL) bits */
+#define MSCAN_TX2              0x04
+#define MSCAN_TX1              0x02
+#define MSCAN_TX0              0x01
+
+/* MSCAN ID acceptance control register (CANIDAC) bits */
+#define MSCAN_IDAM1            0x20
+#define MSCAN_IDAM0            0x10
+#define MSCAN_IDHIT2   0x04
+#define MSCAN_IDHIT1   0x02
+#define MSCAN_IDHIT0   0x01
+
+#define MSCAN_AF_32BIT 0x00
+#define MSCAN_AF_16BIT MSCAN_IDAM0
+#define MSCAN_AF_8BIT  MSCAN_IDAM1
+#define MSCAN_AF_CLOSED        (MSCAN_IDAM0|MSCAN_IDAM1)
+#define MSCAN_AF_MASK  (~(MSCAN_IDAM0|MSCAN_IDAM1))
+
+/* MSCAN Miscellaneous Register (CANMISC) bits */
+#define MSCAN_BOHOLD   0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+ #define _MSCAN_RESERVED_(n,num)       u8      _res##n[num]
+ #define _MSCAN_RESERVED_DSR_SIZE      2
+#else
+ #define _MSCAN_RESERVED_(n,num)
+ #define _MSCAN_RESERVED_DSR_SIZE      0
+#endif
+
+/* Structure of the hardware registers */
+struct mscan_regs {
+/*======================================================
+       register map for mscan
+========================================================*/
+                                                       /*  MPC5200        MSCAN block          */
+                                                       /*                        see doco S12MSCANV3/D */
+       u8  canctl0;            /* + 0x00                  0x00 */
+       u8  canctl1;            /* + 0x01                  0x01 */
+       _MSCAN_RESERVED_(1,2);  /* + 0x02                               */
+       u8  canbtr0;            /* + 0x04                  0x02 */
+       u8  canbtr1;            /* + 0x05                  0x03 */
+       _MSCAN_RESERVED_(2,2);  /* + 0x06                               */
+       u8  canrflg;            /* + 0x08                  0x04 */
+       u8  canrier;            /* + 0x09                  0x05 */
+       _MSCAN_RESERVED_(3,2);  /* + 0x0a                               */
+       u8  cantflg;            /* + 0x0c                  0x06 */
+       u8  cantier;            /* + 0x0d                  0x07 */
+       _MSCAN_RESERVED_(4,2);  /* + 0x0e                               */
+       u8  cantarq;            /* + 0x10                  0x08 */
+       u8  cantaak;            /* + 0x11                  0x09 */
+       _MSCAN_RESERVED_(5,2);  /* + 0x12                               */
+       u8  cantbsel;           /* + 0x14                  0x0a */
+       u8  canidac;            /* + 0x15                  0x0b */
+       u8  reserved;                   /* + 0x16                  0x0c */
+       _MSCAN_RESERVED_(6,5);  /* + 0x17                               */
+#ifndef CONFIG_PPC_MPC52xx
+       u8  canmisc;                    /*                                 0x0d */
+#endif
+       u8  canrxerr;                   /* + 0x1c                  0x0e */
+       u8  cantxerr;                   /* + 0x1d                  0x0f */
+       _MSCAN_RESERVED_(7,2);  /* + 0x1e                               */
+       u16     canidar1_0;                     /* + 0x20                  0x10 */
+       _MSCAN_RESERVED_(8,2);  /* + 0x22                               */
+       u16     canidar3_2;             /* + 0x24                  0x12 */
+       _MSCAN_RESERVED_(9,2);  /* + 0x26                               */
+       u16     canidmr1_0;                     /* + 0x28                  0x14 */
+       _MSCAN_RESERVED_(10,2); /* + 0x2a                               */
+       u16     canidmr3_2;                     /* + 0x2c                  0x16 */
+       _MSCAN_RESERVED_(11,2); /* + 0x2e                               */
+       u16     canidar5_4;             /* + 0x30                  0x18 */
+       _MSCAN_RESERVED_(12,2); /* + 0x32                               */
+       u16     canidar7_6;             /* + 0x34                  0x1a */
+       _MSCAN_RESERVED_(13,2); /* + 0x36                               */
+       u16     canidmr5_4;             /* + 0x38                  0x1c */
+       _MSCAN_RESERVED_(14,2); /* + 0x3a                               */
+       u16     canidmr7_6;             /* + 0x3c                  0x1e */
+       _MSCAN_RESERVED_(15,2); /* + 0x3e                               */
+       struct  {
+               u16 idr1_0;                     /* + 0x40                  0x20 */
+               _MSCAN_RESERVED_(16,2); /* + 0x42                               */
+               u16 idr3_2;                     /* + 0x44                  0x22 */
+               _MSCAN_RESERVED_(17,2); /* + 0x46                               */
+               u16 dsr1_0;                     /* + 0x48                  0x24 */
+               _MSCAN_RESERVED_(18,2); /* + 0x4a                               */
+               u16 dsr3_2;                     /* + 0x4c                  0x26 */
+               _MSCAN_RESERVED_(19,2); /* + 0x4e                               */
+               u16 dsr5_4;                     /* + 0x50                  0x28 */
+               _MSCAN_RESERVED_(20,2); /* + 0x52                               */
+               u16 dsr7_6;                     /* + 0x54                  0x2a */
+               _MSCAN_RESERVED_(21,2); /* + 0x56                               */
+               u8  dlr;                        /* + 0x58                  0x2c */
+               u8      :8;                             /* + 0x59                  0x2d */
+               _MSCAN_RESERVED_(22,2); /* + 0x5a                               */
+               u16 time;                       /* + 0x5c                  0x2e */
+       } rx;
+       _MSCAN_RESERVED_(23,2);         /* + 0x5e                               */
+       struct  {
+               u16  idr1_0;                    /* + 0x60                  0x30 */
+               _MSCAN_RESERVED_(24,2); /* + 0x62                               */
+               u16  idr3_2;                    /* + 0x64                  0x32 */
+               _MSCAN_RESERVED_(25,2); /* + 0x66                               */
+               u16  dsr1_0;                    /* + 0x68                  0x34 */
+               _MSCAN_RESERVED_(26,2); /* + 0x6a                               */
+               u16  dsr3_2;                    /* + 0x6c                  0x36 */
+               _MSCAN_RESERVED_(27,2); /* + 0x6e                               */
+               u16  dsr5_4;                    /* + 0x70                  0x38 */
+               _MSCAN_RESERVED_(28,2); /* + 0x72                               */
+               u16  dsr7_6;                    /* + 0x74                  0x3a */
+               _MSCAN_RESERVED_(29,2); /* + 0x76                               */
+               u8  dlr;                        /* + 0x78                  0x3c */
+               u8  tbpr;                       /* + 0x79                  0x3d */
+               _MSCAN_RESERVED_(30,2); /* + 0x7a                               */
+               u16 time;                       /* + 0x7c                  0x3e */
+       } tx;
+       _MSCAN_RESERVED_(31,2);     /* + 0x7e                           */
+} __attribute__ ((packed));
+
+#undef _MSCAN_RESERVED_
+#define MSCAN_REGION   sizeof(struct mscan)
+
+#define MSCAN_WATCHDOG_TIMEOUT ((500*HZ)/1000)
+
+struct mscan_platform_data {
+       u8 clock_src;   /* MSCAN_CLKSRC_BUS or MSCAN_CLKSRC_XTAL */
+       u32 clock_frq;  /* can ref. clock, in Hz */
+};
+
+struct can_device *alloc_mscandev(void);
+/* @clock_src:
+       1 = The MSCAN clock source is the onchip Bus Clock.
+       0 = The MSCAN clock source is the chip Oscillator Clock.
+*/
+extern int mscan_register(struct can_device *can, int clock_src);
+extern void mscan_unregister(struct can_device *can);
+
+#endif /* __MSCAN_H__ */