--- /dev/null
+/*
+ * $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");
--- /dev/null
+/*
+ * $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(®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 )
+ break;
+ udelay(100);
+ }
+ if(i>255)
+ 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 )
+ break;
+ }
+ if(i>255)
+ ret=-ENODEV;
+ }
+
+ if ( !ret && (mode & MSCAN_CSWAI) )
+ out_8( ®s->canctl0, in_8(®s->canctl0) | MSCAN_CSWAI);
+
+ } else {
+ canctl1 = in_8(®s->canctl1);
+ if ( canctl1 & (MSCAN_SLPAK | MSCAN_INITAK) ) {
+ out_8(®s->canctl0, in_8(®s->canctl0) &
+ ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+ for (i = 0; i < 255; i++ ) {
+ canctl1 = in_8(®s->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(®s->canctl0) &
+ (MSCAN_SLPRQ | MSCAN_INITRQ | MSCAN_CSWAI);
+ state->canrier = in_8(®s->canrier);
+ state->cantier = in_8(®s->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( ®s->canrier, state->canrier);
+ out_8( ®s->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(®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)
+ set_bit(F_TX_WAIT_ALL, &priv->flags);
+ netif_stop_queue(ndev);
+ case 2:
+ set_bit(F_TX_PROGRESS, &priv->flags);
+ }
+ 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 )
+ 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);
+ } 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(®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++) {
+ out_be16(data, *payload++);
+ data += 2+_MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(®s->tx.dlr, frame->can_dlc);
+ out_8(®s->tx.tbpr, priv->cur_pri);
+
+ /* Start transmission. */
+ out_8(®s->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( ®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);
+ 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);
+
+}
+
+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(®s->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(®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 >>= 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(®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++) {
+ *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(®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 ) {
+ 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(®s->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(®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;
+}
+
+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(®s->cantier) & MSCAN_TXE )
+ {
+ struct list_head *tmp, *pos;
+
+ cantflg = in_8(®s->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(®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++;
+ }
+ 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( ®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;
+ }
+ if (canrflg & ~MSCAN_STAT_MSK) {
+ priv->shadow_canrier = in_8(®s->canrier);
+ out_8(®s->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(®s->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(®s->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(®s->canidar1_0, 0);
+ out_be16(®s->canidar3_2, 0);
+ out_be16(®s->canidar5_4, 0);
+ out_be16(®s->canidar7_6, 0);
+
+ 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);
+
+ out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_LISTEN);
+ mscan_set_mode( can, MSCAN_NORMAL_MODE);
+
+ priv->shadow_statflg = in_8(®s->canrflg) & MSCAN_STAT_MSK;
+ priv->cur_pri = 0;
+ priv->tx_active = 0;
+
+ 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);
+
+ 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(®s->cantier, 0);
+ out_8(®s->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(®s->canctl1);
+ if(clock_src)
+ ctl1 |= MSCAN_CLKSRC;
+ else
+ ctl1 &= ~MSCAN_CLKSRC;
+
+ ctl1 |= MSCAN_CANE;
+ out_8(®s->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(®s->canctl1, in_8(®s->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");
--- /dev/null
+/*
+ * $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__ */