lkml.org 
[lkml]   [2010]   [Mar]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
    Date
    The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
    MODULbus carrier board. It is an intelligent CAN controller with a
    microcontroller and associated firmware.

    Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
    Cc: socketcan-core@lists.berlios.de
    ---
    drivers/net/can/Kconfig | 10 +
    drivers/net/can/Makefile | 1 +
    drivers/net/can/janz-ican3.c | 1659 ++++++++++++++++++++++++++++++++++++++++++
    3 files changed, 1670 insertions(+), 0 deletions(-)
    create mode 100644 drivers/net/can/janz-ican3.c

    diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
    index 05b7517..2c5227c 100644
    --- a/drivers/net/can/Kconfig
    +++ b/drivers/net/can/Kconfig
    @@ -63,6 +63,16 @@ config CAN_BFIN
    To compile this driver as a module, choose M here: the
    module will be called bfin_can.

    +config CAN_JANZ_ICAN3
    + tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
    + depends on CAN_DEV && MFD_JANZ_CMODIO
    + ---help---
    + Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
    + connects to a MODULbus carrier board.
    +
    + This driver can also be built as a module. If so, the module will be
    + called janz-ican3.ko.
    +
    source "drivers/net/can/mscan/Kconfig"

    source "drivers/net/can/sja1000/Kconfig"
    diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
    index 7a702f2..9047cd0 100644
    --- a/drivers/net/can/Makefile
    +++ b/drivers/net/can/Makefile
    @@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91) += at91_can.o
    obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
    obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
    obj-$(CONFIG_CAN_BFIN) += bfin_can.o
    +obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o

    ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
    diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
    new file mode 100644
    index 0000000..94d4995
    --- /dev/null
    +++ b/drivers/net/can/janz-ican3.c
    @@ -0,0 +1,1659 @@
    +/*
    + * Janz MODULbus VMOD-ICAN3 CAN Interface Driver
    + *
    + * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
    + *
    + * 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.
    + */
    +
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/init.h>
    +#include <linux/interrupt.h>
    +#include <linux/delay.h>
    +#include <linux/platform_device.h>
    +
    +#include <linux/netdevice.h>
    +#include <linux/can.h>
    +#include <linux/can/dev.h>
    +#include <linux/can/error.h>
    +
    +#include <linux/mfd/janz.h>
    +
    +/* the DPM has 64k of memory, organized into 256x 256 byte pages */
    +#define DPM_NUM_PAGES 256
    +#define DPM_PAGE_SIZE 256
    +#define DPM_PAGE_ADDR(p) ((p) * DPM_PAGE_SIZE)
    +
    +/* JANZ ICAN3 "old-style" host interface queue page numbers */
    +#define QUEUE_OLD_CONTROL 0
    +#define QUEUE_OLD_RB0 1
    +#define QUEUE_OLD_RB1 2
    +#define QUEUE_OLD_WB0 3
    +#define QUEUE_OLD_WB1 4
    +
    +/* Janz ICAN3 "old-style" host interface control registers */
    +#define MSYNC_PEER 0x00 /* ICAN only */
    +#define MSYNC_LOCL 0x01 /* host only */
    +#define TARGET_RUNNING 0x02
    +
    +#define MSYNC_RB0 0x01
    +#define MSYNC_RB1 0x02
    +#define MSYNC_RBLW 0x04
    +#define MSYNC_RB_MASK (MSYNC_RB0 | MSYNC_RB1)
    +
    +#define MSYNC_WB0 0x10
    +#define MSYNC_WB1 0x20
    +#define MSYNC_WBLW 0x40
    +#define MSYNC_WB_MASK (MSYNC_WB0 | MSYNC_WB1)
    +
    +/* Janz ICAN3 "new-style" host interface queue page numbers */
    +#define QUEUE_TOHOST 5
    +#define QUEUE_FROMHOST_MID 6
    +#define QUEUE_FROMHOST_HIGH 7
    +#define QUEUE_FROMHOST_LOW 8
    +
    +/* The first free page in the DPM is #9 */
    +#define DPM_FREE_START 9
    +
    +/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */
    +#define DESC_VALID 0x80
    +#define DESC_WRAP 0x40
    +#define DESC_INTERRUPT 0x20
    +#define DESC_IVALID 0x10
    +#define DESC_LEN(len) (len)
    +
    +/* Janz ICAN3 Firmware Messages */
    +#define MSG_CONNECTI 0x02
    +#define MSG_DISCONNECT 0x03
    +#define MSG_IDVERS 0x04
    +#define MSG_MSGLOST 0x05
    +#define MSG_NEWHOSTIF 0x08
    +#define MSG_SETAFILMASK 0x10
    +#define MSG_INITFDPMQUEUE 0x11
    +#define MSG_HWCONF 0x12
    +#define MSG_FMSGLOST 0x15
    +#define MSG_CEVTIND 0x37
    +#define MSG_CBTRREQ 0x41
    +#define MSG_COFFREQ 0x42
    +#define MSG_CONREQ 0x43
    +#define MSG_CCONFREQ 0x47
    +
    +/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */
    +#define SETAFILMASK_REJECT 0x00
    +#define SETAFILMASK_FASTIF 0x02
    +
    +/* Janz ICAN3 CAN Hardware Configuration Message Types */
    +#define HWCONF_TERMINATE_ON 0x01
    +#define HWCONF_TERMINATE_OFF 0x00
    +
    +/* Janz ICAN3 CAN Event Indication Message Types */
    +#define CEVTIND_EI 0x01
    +#define CEVTIND_DOI 0x02
    +#define CEVTIND_LOST 0x04
    +#define CEVTIND_FULL 0x08
    +#define CEVTIND_BEI 0x10
    +
    +#define CEVTIND_CHIP_SJA1000 0x02
    +
    +#define ICAN3_BUSERR_QUOTA_MAX 255
    +
    +/* Janz ICAN3 CAN Frame Conversion */
    +#define ICAN3_ECHO 0x10
    +#define ICAN3_EFF_RTR 0x40
    +#define ICAN3_SFF_RTR 0x10
    +#define ICAN3_EFF 0x80
    +
    +#define ICAN3_CAN_TYPE_MASK 0x0f
    +#define ICAN3_CAN_TYPE_SFF 0x00
    +#define ICAN3_CAN_TYPE_EFF 0x01
    +
    +#define ICAN3_CAN_DLC_MASK 0x0f
    +
    +/*
    + * SJA1000 Status and Error Register Definitions
    + *
    + * Copied from drivers/net/can/sja1000/sja1000.h
    + */
    +
    +/* status register content */
    +#define SR_BS 0x80
    +#define SR_ES 0x40
    +#define SR_TS 0x20
    +#define SR_RS 0x10
    +#define SR_TCS 0x08
    +#define SR_TBS 0x04
    +#define SR_DOS 0x02
    +#define SR_RBS 0x01
    +
    +#define SR_CRIT (SR_BS|SR_ES)
    +
    +/* ECC register */
    +#define ECC_SEG 0x1F
    +#define ECC_DIR 0x20
    +#define ECC_ERR 6
    +#define ECC_BIT 0x00
    +#define ECC_FORM 0x40
    +#define ECC_STUFF 0x80
    +#define ECC_MASK 0xc0
    +
    +/* Number of buffers for use in the "new-style" host interface */
    +#define ICAN3_NEW_BUFFERS 16
    +
    +/* Number of buffers for use in the "fast" host interface */
    +#define ICAN3_TX_BUFFERS 512
    +#define ICAN3_RX_BUFFERS 1024
    +
    +/* Driver Name */
    +#define DRV_NAME "janz-ican3"
    +
    +/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */
    +struct ican3_dpm_control {
    + /* window address register */
    + u8 window_address;
    + u8 unused1;
    +
    + /*
    + * Read access: clear interrupt from microcontroller
    + * Write access: send interrupt to microcontroller
    + */
    + u8 interrupt;
    + u8 unused2;
    +
    + /* write-only: reset all hardware on the module */
    + u8 hwreset;
    + u8 unused3;
    +
    + /* write-only: generate an interrupt to the TPU */
    + u8 tpuinterrupt;
    +};
    +
    +struct ican3_dev {
    +
    + /* must be the first member */
    + struct can_priv can;
    +
    + /* CAN network device */
    + struct net_device *ndev;
    + struct napi_struct napi;
    +
    + /* Device for printing */
    + struct device *dev;
    +
    + /* module number */
    + unsigned int num;
    +
    + /* base address of registers and IRQ */
    + struct janz_cmodio_onboard_regs __iomem *ctrl;
    + struct ican3_dpm_control *dpmctrl;
    + void __iomem *dpm;
    + int irq;
    +
    + /* old and new style host interface */
    + unsigned int iftype;
    + spinlock_t lock;
    +
    + /* new host interface */
    + unsigned int rx_int;
    + unsigned int rx_num;
    + unsigned int tx_num;
    +
    + /* fast host interface */
    + unsigned int fastrx_start;
    + unsigned int fastrx_int;
    + unsigned int fastrx_num;
    + unsigned int fasttx_start;
    + unsigned int fasttx_num;
    +
    + /* first free DPM page */
    + unsigned int free_page;
    +};
    +
    +struct ican3_msg {
    + u8 control;
    + u8 spec;
    + __le16 len;
    + u8 data[252];
    +};
    +
    +struct ican3_new_desc {
    + u8 control;
    + u8 pointer;
    +};
    +
    +struct ican3_fast_desc {
    + u8 control;
    + u8 command;
    + u8 data[14];
    +};
    +
    +/* write to the window basic address register */
    +static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page)
    +{
    + BUG_ON(page >= DPM_NUM_PAGES);
    + iowrite8(page, &mod->dpmctrl->window_address);
    +}
    +
    +/*
    + * ICAN3 "old-style" host interface
    + */
    +
    +/*
    + * Recieve a message from the ICAN3 "old-style" firmware interface
    + *
    + * LOCKING: must hold mod->lock
    + *
    + * returns 0 on success, -ENOMEM when no message exists
    + */
    +static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + unsigned int mbox, mbox_page;
    + u8 locl, peer, xord;
    +
    + /* get the MSYNC registers */
    + ican3_set_page(mod, QUEUE_OLD_CONTROL);
    + peer = ioread8(mod->dpm + MSYNC_PEER);
    + locl = ioread8(mod->dpm + MSYNC_LOCL);
    + xord = locl ^ peer;
    +
    + if ((xord & MSYNC_RB_MASK) == 0x00) {
    + dev_dbg(mod->dev, "no mbox for reading\n");
    + return -ENOMEM;
    + }
    +
    + /* find the first free mbox to read */
    + if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK)
    + mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1;
    + else
    + mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1;
    +
    + /* copy the message */
    + mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1;
    + ican3_set_page(mod, mbox_page);
    + memcpy_fromio(msg, mod->dpm, sizeof(*msg));
    +
    + /*
    + * notify the firmware that the read buffer is available
    + * for it to fill again
    + */
    + locl ^= mbox;
    +
    + ican3_set_page(mod, QUEUE_OLD_CONTROL);
    + iowrite8(locl, mod->dpm + MSYNC_LOCL);
    + return 0;
    +}
    +
    +/*
    + * Send a message through the "old-style" firmware interface
    + *
    + * LOCKING: must hold mod->lock
    + *
    + * returns 0 on success, -ENOMEM when no free space exists
    + */
    +static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + unsigned int mbox, mbox_page;
    + u8 locl, peer, xord;
    +
    + /* get the MSYNC registers */
    + ican3_set_page(mod, QUEUE_OLD_CONTROL);
    + peer = ioread8(mod->dpm + MSYNC_PEER);
    + locl = ioread8(mod->dpm + MSYNC_LOCL);
    + xord = locl ^ peer;
    +
    + if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) {
    + dev_err(mod->dev, "no mbox for writing\n");
    + return -ENOMEM;
    + }
    +
    + /* calculate a free mbox to use */
    + mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0;
    +
    + /* copy the message to the DPM */
    + mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1;
    + ican3_set_page(mod, mbox_page);
    + memcpy_toio(mod->dpm, msg, sizeof(*msg));
    +
    + locl ^= mbox;
    + if (mbox == MSYNC_WB1)
    + locl |= MSYNC_WBLW;
    +
    + ican3_set_page(mod, QUEUE_OLD_CONTROL);
    + iowrite8(locl, mod->dpm + MSYNC_LOCL);
    + return 0;
    +}
    +
    +/*
    + * ICAN3 "new-style" Host Interface Setup
    + */
    +
    +static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod)
    +{
    + struct ican3_new_desc desc;
    + unsigned long flags;
    + void __iomem *dst;
    + int i;
    +
    + spin_lock_irqsave(&mod->lock, flags);
    +
    + /* setup the internal datastructures for RX */
    + mod->rx_num = 0;
    + mod->rx_int = 0;
    +
    + /* tohost queue descriptors are in page 5 */
    + ican3_set_page(mod, QUEUE_TOHOST);
    + dst = mod->dpm;
    +
    + /* initialize the tohost (rx) queue descriptors: pages 9-24 */
    + for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
    + desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */
    + desc.pointer = mod->free_page;
    +
    + /* set wrap flag on last buffer */
    + if (i == ICAN3_NEW_BUFFERS - 1)
    + desc.control |= DESC_WRAP;
    +
    + memcpy_toio(dst, &desc, sizeof(desc));
    + dst += sizeof(desc);
    + mod->free_page++;
    + }
    +
    + /* fromhost (tx) mid queue descriptors are in page 6 */
    + ican3_set_page(mod, QUEUE_FROMHOST_MID);
    + dst = mod->dpm;
    +
    + /* setup the internal datastructures for TX */
    + mod->tx_num = 0;
    +
    + /* initialize the fromhost mid queue descriptors: pages 25-40 */
    + for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
    + desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */
    + desc.pointer = mod->free_page;
    +
    + /* set wrap flag on last buffer */
    + if (i == ICAN3_NEW_BUFFERS - 1)
    + desc.control |= DESC_WRAP;
    +
    + memcpy_toio(dst, &desc, sizeof(desc));
    + dst += sizeof(desc);
    + mod->free_page++;
    + }
    +
    + /* fromhost hi queue descriptors are in page 7 */
    + ican3_set_page(mod, QUEUE_FROMHOST_HIGH);
    + dst = mod->dpm;
    +
    + /* initialize only a single buffer in the fromhost hi queue (unused) */
    + desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
    + desc.pointer = mod->free_page;
    + memcpy_toio(dst, &desc, sizeof(desc));
    + mod->free_page++;
    +
    + /* fromhost low queue descriptors are in page 8 */
    + ican3_set_page(mod, QUEUE_FROMHOST_LOW);
    + dst = mod->dpm;
    +
    + /* initialize only a single buffer in the fromhost low queue (unused) */
    + desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
    + desc.pointer = mod->free_page;
    + memcpy_toio(dst, &desc, sizeof(desc));
    + mod->free_page++;
    +
    + spin_unlock_irqrestore(&mod->lock, flags);
    +}
    +
    +/*
    + * ICAN3 Fast Host Interface Setup
    + */
    +
    +static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod)
    +{
    + struct ican3_fast_desc desc;
    + unsigned long flags;
    + unsigned int addr;
    + void __iomem *dst;
    + int i;
    +
    + spin_lock_irqsave(&mod->lock, flags);
    +
    + /* save the start recv page */
    + mod->fastrx_start = mod->free_page;
    + mod->fastrx_num = 0;
    + mod->fastrx_int = 0;
    +
    + /* build a single fast tohost queue descriptor */
    + memset(&desc, 0, sizeof(desc));
    + desc.control = 0x00;
    + desc.command = 1;
    +
    + /* build the tohost queue descriptor ring in memory */
    + addr = 0;
    + for (i = 0; i < ICAN3_RX_BUFFERS; i++) {
    +
    + /* set the wrap bit on the last buffer */
    + if (i == ICAN3_RX_BUFFERS - 1)
    + desc.control |= DESC_WRAP;
    +
    + /* switch to the correct page */
    + ican3_set_page(mod, mod->free_page);
    +
    + /* copy the descriptor to the DPM */
    + dst = mod->dpm + addr;
    + memcpy_toio(dst, &desc, sizeof(desc));
    + addr += sizeof(desc);
    +
    + /* move to the next page if necessary */
    + if (addr >= DPM_PAGE_SIZE) {
    + addr = 0;
    + mod->free_page++;
    + }
    + }
    +
    + /* make sure we page-align the next queue */
    + if (addr != 0)
    + mod->free_page++;
    +
    + /* save the start xmit page */
    + mod->fasttx_start = mod->free_page;
    + mod->fasttx_num = 0;
    +
    + /* build a single fast fromhost queue descriptor */
    + memset(&desc, 0, sizeof(desc));
    + desc.control = DESC_VALID;
    + desc.command = 1;
    +
    + /* build the fromhost queue descriptor ring in memory */
    + addr = 0;
    + for (i = 0; i < ICAN3_TX_BUFFERS; i++) {
    +
    + /* set the wrap bit on the last buffer */
    + if (i == ICAN3_TX_BUFFERS - 1)
    + desc.control |= DESC_WRAP;
    +
    + /* switch to the correct page */
    + ican3_set_page(mod, mod->free_page);
    +
    + /* copy the descriptor to the DPM */
    + dst = mod->dpm + addr;
    + memcpy_toio(dst, &desc, sizeof(desc));
    + addr += sizeof(desc);
    +
    + /* move to the next page if necessary */
    + if (addr >= DPM_PAGE_SIZE) {
    + addr = 0;
    + mod->free_page++;
    + }
    + }
    +
    + spin_unlock_irqrestore(&mod->lock, flags);
    +}
    +
    +/*
    + * ICAN3 "new-style" Host Interface Message Helpers
    + */
    +
    +/*
    + * LOCKING: must hold mod->lock
    + */
    +static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + struct ican3_new_desc desc;
    + void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc));
    +
    + /* switch to the fromhost mid queue, and read the buffer descriptor */
    + ican3_set_page(mod, QUEUE_FROMHOST_MID);
    + memcpy_fromio(&desc, desc_addr, sizeof(desc));
    +
    + if (!(desc.control & DESC_VALID)) {
    + dev_dbg(mod->dev, "%s: no free buffers\n", __func__);
    + return -ENOMEM;
    + }
    +
    + /* switch to the data page, copy the data */
    + ican3_set_page(mod, desc.pointer);
    + memcpy_toio(mod->dpm, msg, sizeof(*msg));
    +
    + /* switch back to the descriptor, set the valid bit, write it back */
    + ican3_set_page(mod, QUEUE_FROMHOST_MID);
    + desc.control ^= DESC_VALID;
    + memcpy_toio(desc_addr, &desc, sizeof(desc));
    +
    + /* update the tx number */
    + mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1);
    + return 0;
    +}
    +
    +/*
    + * LOCKING: must hold mod->lock
    + */
    +static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + struct ican3_new_desc desc;
    + void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc));
    +
    + /* switch to the tohost queue, and read the buffer descriptor */
    + ican3_set_page(mod, QUEUE_TOHOST);
    + memcpy_fromio(&desc, desc_addr, sizeof(desc));
    +
    + if (!(desc.control & DESC_VALID)) {
    + dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__);
    + return -ENOMEM;
    + }
    +
    + /* switch to the data page, copy the data */
    + ican3_set_page(mod, desc.pointer);
    + memcpy_fromio(msg, mod->dpm, sizeof(*msg));
    +
    + /* switch back to the descriptor, toggle the valid bit, write it back */
    + ican3_set_page(mod, QUEUE_TOHOST);
    + desc.control ^= DESC_VALID;
    + memcpy_toio(desc_addr, &desc, sizeof(desc));
    +
    + /* update the rx number */
    + mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1);
    + return 0;
    +}
    +
    +/*
    + * Message Send / Recv Helpers
    + */
    +
    +static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + unsigned long flags;
    + int ret;
    +
    + spin_lock_irqsave(&mod->lock, flags);
    +
    + if (mod->iftype == 0)
    + ret = ican3_old_send_msg(mod, msg);
    + else
    + ret = ican3_new_send_msg(mod, msg);
    +
    + spin_unlock_irqrestore(&mod->lock, flags);
    + return ret;
    +}
    +
    +static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + unsigned long flags;
    + int ret;
    +
    + spin_lock_irqsave(&mod->lock, flags);
    +
    + if (mod->iftype == 0)
    + ret = ican3_old_recv_msg(mod, msg);
    + else
    + ret = ican3_new_recv_msg(mod, msg);
    +
    + spin_unlock_irqrestore(&mod->lock, flags);
    + return ret;
    +}
    +
    +/*
    + * Quick Pre-constructed Messages
    + */
    +
    +static int __devinit ican3_msg_connect(struct ican3_dev *mod)
    +{
    + struct ican3_msg msg;
    +
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = MSG_CONNECTI;
    + msg.len = cpu_to_le16(0);
    +
    + return ican3_send_msg(mod, &msg);
    +}
    +
    +static int __devexit ican3_msg_disconnect(struct ican3_dev *mod)
    +{
    + struct ican3_msg msg;
    +
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = MSG_DISCONNECT;
    + msg.len = cpu_to_le16(0);
    +
    + return ican3_send_msg(mod, &msg);
    +}
    +
    +static int __devinit ican3_msg_newhostif(struct ican3_dev *mod)
    +{
    + struct ican3_msg msg;
    + int ret;
    +
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = MSG_NEWHOSTIF;
    + msg.len = cpu_to_le16(0);
    +
    + /* If we're not using the old interface, switching seems bogus */
    + WARN_ON(mod->iftype != 0);
    +
    + ret = ican3_send_msg(mod, &msg);
    + if (ret)
    + return ret;
    +
    + /* mark the module as using the new host interface */
    + mod->iftype = 1;
    + return 0;
    +}
    +
    +static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod)
    +{
    + struct ican3_msg msg;
    + unsigned int addr;
    +
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = MSG_INITFDPMQUEUE;
    + msg.len = cpu_to_le16(8);
    +
    + /* write the tohost queue start address */
    + addr = DPM_PAGE_ADDR(mod->fastrx_start);
    + msg.data[0] = addr & 0xff;
    + msg.data[1] = (addr >> 8) & 0xff;
    + msg.data[2] = (addr >> 16) & 0xff;
    + msg.data[3] = (addr >> 24) & 0xff;
    +
    + /* write the fromhost queue start address */
    + addr = DPM_PAGE_ADDR(mod->fasttx_start);
    + msg.data[4] = addr & 0xff;
    + msg.data[5] = (addr >> 8) & 0xff;
    + msg.data[6] = (addr >> 16) & 0xff;
    + msg.data[7] = (addr >> 24) & 0xff;
    +
    + /* If we're not using the new interface yet, we cannot do this */
    + WARN_ON(mod->iftype != 1);
    +
    + return ican3_send_msg(mod, &msg);
    +}
    +
    +/*
    + * Setup the CAN filter to either accept or reject all
    + * messages from the CAN bus.
    + */
    +static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept)
    +{
    + struct ican3_msg msg;
    + int ret;
    +
    + /* Standard Frame Format */
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = MSG_SETAFILMASK;
    + msg.len = cpu_to_le16(5);
    + msg.data[0] = 0x00; /* IDLo LSB */
    + msg.data[1] = 0x00; /* IDLo MSB */
    + msg.data[2] = 0xff; /* IDHi LSB */
    + msg.data[3] = 0x07; /* IDHi MSB */
    +
    + /* accept all frames for fast host if, or reject all frames */
    + msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
    +
    + ret = ican3_send_msg(mod, &msg);
    + if (ret)
    + return ret;
    +
    + /* Extended Frame Format */
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = MSG_SETAFILMASK;
    + msg.len = cpu_to_le16(13);
    + msg.data[0] = 0; /* MUX = 0 */
    + msg.data[1] = 0x00; /* IDLo LSB */
    + msg.data[2] = 0x00;
    + msg.data[3] = 0x00;
    + msg.data[4] = 0x20; /* IDLo MSB */
    + msg.data[5] = 0xff; /* IDHi LSB */
    + msg.data[6] = 0xff;
    + msg.data[7] = 0xff;
    + msg.data[8] = 0x3f; /* IDHi MSB */
    +
    + /* accept all frames for fast host if, or reject all frames */
    + msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
    +
    + return ican3_send_msg(mod, &msg);
    +}
    +
    +/*
    + * Bring the CAN bus online or offline
    + */
    +static int ican3_set_bus_state(struct ican3_dev *mod, bool on)
    +{
    + struct ican3_msg msg;
    +
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = on ? MSG_CONREQ : MSG_COFFREQ;
    + msg.len = cpu_to_le16(0);
    +
    + return ican3_send_msg(mod, &msg);
    +}
    +
    +static int __devinit ican3_set_termination(struct ican3_dev *mod, bool on)
    +{
    + struct ican3_msg msg;
    +
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = MSG_HWCONF;
    + msg.len = cpu_to_le16(2);
    + msg.data[0] = 0x00;
    + msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF;
    +
    + return ican3_send_msg(mod, &msg);
    +}
    +
    +static int __devinit ican3_set_buserror(struct ican3_dev *mod, u8 quota)
    +{
    + struct ican3_msg msg;
    +
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = MSG_CCONFREQ;
    + msg.len = cpu_to_le16(2);
    + msg.data[0] = 0x00;
    + msg.data[1] = quota;
    +
    + return ican3_send_msg(mod, &msg);
    +}
    +
    +/*
    + * ICAN3 to Linux CAN Frame Conversion
    + */
    +
    +static void ican3_to_can_frame(struct ican3_dev *mod,
    + struct ican3_fast_desc *desc,
    + struct can_frame *cf)
    +{
    + if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
    + dev_dbg(mod->dev, "%s: old frame format\n", __func__);
    + if (desc->data[1] & ICAN3_SFF_RTR)
    + cf->can_id |= CAN_RTR_FLAG;
    +
    + cf->can_id |= desc->data[0] << 3;
    + cf->can_id |= (desc->data[1] & 0xe0) >> 5;
    + cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
    + memcpy(cf->data, &desc->data[2], sizeof(cf->data));
    + } else {
    + dev_dbg(mod->dev, "%s: new frame format\n", __func__);
    + cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
    + if (desc->data[0] & ICAN3_EFF_RTR)
    + cf->can_id |= CAN_RTR_FLAG;
    +
    + if (desc->data[0] & ICAN3_EFF) {
    + cf->can_id |= CAN_EFF_FLAG;
    + cf->can_id |= desc->data[2] << 21; /* 28-21 */
    + cf->can_id |= desc->data[3] << 13; /* 20-13 */
    + cf->can_id |= desc->data[4] << 5; /* 12-5 */
    + cf->can_id |= (desc->data[5] & 0xf8) >> 3;
    + } else {
    + cf->can_id |= desc->data[2] << 3; /* 10-3 */
    + cf->can_id |= desc->data[3] >> 5; /* 2-0 */
    + }
    +
    + memcpy(cf->data, &desc->data[6], sizeof(cf->data));
    + }
    +}
    +
    +static void can_frame_to_ican3(struct ican3_dev *mod,
    + struct can_frame *cf,
    + struct ican3_fast_desc *desc)
    +{
    + /* clear out any stale data in the descriptor */
    + memset(desc->data, 0, sizeof(desc->data));
    +
    + /* we always use the extended format, with the ECHO flag set */
    + desc->command = ICAN3_CAN_TYPE_EFF;
    + desc->data[0] |= cf->can_dlc;
    + desc->data[1] |= ICAN3_ECHO;
    +
    + if (cf->can_id & CAN_RTR_FLAG)
    + desc->data[0] |= ICAN3_EFF_RTR;
    +
    + /* pack the id into the correct places */
    + if (cf->can_id & CAN_EFF_FLAG) {
    + dev_dbg(mod->dev, "%s: extended frame\n", __func__);
    + desc->data[0] |= ICAN3_EFF;
    + desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
    + desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
    + desc->data[4] = (cf->can_id & 0x00001fe0) >> 5; /* 12-5 */
    + desc->data[5] = (cf->can_id & 0x0000001f) << 3; /* 4-0 */
    + } else {
    + dev_dbg(mod->dev, "%s: standard frame\n", __func__);
    + desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
    + desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0 */
    + }
    +
    + /* copy the data bits into the descriptor */
    + memcpy(&desc->data[6], cf->data, sizeof(cf->data));
    +}
    +
    +/*
    + * Interrupt Handling
    + */
    +
    +/*
    + * Handle an ID + Version message response from the firmware. We never generate
    + * this message in production code, but it is very useful when debugging to be
    + * able to display this message.
    + */
    +static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data);
    +}
    +
    +static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + struct net_device *dev = mod->ndev;
    + struct net_device_stats *stats = &dev->stats;
    + struct can_frame *cf;
    + struct sk_buff *skb;
    +
    + /*
    + * Report that communication messages with the microcontroller firmware
    + * are being lost. These are never CAN frames, so we do not generate an
    + * error frame for userspace
    + */
    + if (msg->spec == MSG_MSGLOST) {
    + dev_err(mod->dev, "lost %d control messages\n", msg->data[0]);
    + return;
    + }
    +
    + /*
    + * Oops, this indicates that we have lost messages in the fast queue,
    + * which are exclusively CAN messages. Our driver isn't reading CAN
    + * frames fast enough.
    + *
    + * We'll pretend that the SJA1000 told us that it ran out of buffer
    + * space, because there is not a better message for this.
    + */
    + skb = alloc_can_err_skb(dev, &cf);
    + if (skb) {
    + cf->can_id |= CAN_ERR_CRTL;
    + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
    + stats->rx_errors++;
    + stats->rx_bytes += cf->can_dlc;
    + netif_rx(skb);
    + }
    +}
    +
    +/*
    + * Handle CAN Event Indication Messages from the firmware
    + *
    + * The ICAN3 firmware provides the values of some SJA1000 registers when it
    + * generates this message. The code below is largely copied from the
    + * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
    + */
    +static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + struct net_device *dev = mod->ndev;
    + struct net_device_stats *stats = &dev->stats;
    + enum can_state state = mod->can.state;
    + struct can_frame *cf;
    + struct sk_buff *skb;
    + u8 status, isrc;
    +
    + /* we can only handle the SJA1000 part */
    + if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
    + dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
    + return -ENODEV;
    + }
    +
    + /* check the message length for sanity */
    + if (msg->len < 6) {
    + dev_err(mod->dev, "error message too short\n");
    + return -EINVAL;
    + }
    +
    + skb = alloc_can_err_skb(dev, &cf);
    + if (skb == NULL)
    + return -ENOMEM;
    +
    + isrc = msg->data[0];
    + status = msg->data[3];
    +
    + /* data overrun interrupt */
    + if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
    + cf->can_id |= CAN_ERR_CRTL;
    + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
    + stats->rx_over_errors++;
    + stats->rx_errors++;
    + dev_info(mod->dev, "%s: overflow frame generated\n", __func__);
    + }
    +
    + /* error warning interrupt */
    + if (isrc == CEVTIND_EI) {
    + if (status & SR_BS) {
    + state = CAN_STATE_BUS_OFF;
    + cf->can_id |= CAN_ERR_BUSOFF;
    + can_bus_off(dev);
    + } else if (status & SR_ES) {
    + state = CAN_STATE_ERROR_WARNING;
    + } else {
    + state = CAN_STATE_ERROR_ACTIVE;
    + }
    + }
    +
    + /* bus error interrupt */
    + if (isrc == CEVTIND_BEI) {
    + u8 ecc = msg->data[2];
    + mod->can.can_stats.bus_error++;
    + stats->rx_errors++;
    + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
    +
    + switch (ecc & ECC_MASK) {
    + case ECC_BIT:
    + cf->data[2] |= CAN_ERR_PROT_BIT;
    + break;
    + case ECC_FORM:
    + cf->data[2] |= CAN_ERR_PROT_FORM;
    + break;
    + case ECC_STUFF:
    + cf->data[2] |= CAN_ERR_PROT_STUFF;
    + break;
    + default:
    + cf->data[2] |= CAN_ERR_PROT_UNSPEC;
    + cf->data[3] = ecc & ECC_SEG;
    + break;
    + }
    +
    + if ((ecc & ECC_DIR) == 0)
    + cf->data[2] |= CAN_ERR_PROT_TX;
    + }
    +
    + if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
    + state == CAN_STATE_ERROR_PASSIVE)) {
    + u8 rxerr = msg->data[4];
    + u8 txerr = msg->data[5];
    + cf->can_id |= CAN_ERR_CRTL;
    + if (state == CAN_STATE_ERROR_WARNING) {
    + mod->can.can_stats.error_warning++;
    + cf->data[1] = (txerr > rxerr) ?
    + CAN_ERR_CRTL_TX_WARNING :
    + CAN_ERR_CRTL_RX_WARNING;
    + } else {
    + mod->can.can_stats.error_passive++;
    + cf->data[1] = (txerr > rxerr) ?
    + CAN_ERR_CRTL_TX_PASSIVE :
    + CAN_ERR_CRTL_RX_PASSIVE;
    + }
    + }
    +
    + mod->can.state = state;
    + stats->rx_errors++;
    + stats->rx_bytes += cf->can_dlc;
    + netif_rx(skb);
    + return 0;
    +}
    +
    +static void ican3_handle_unknown_message(struct ican3_dev *mod,
    + struct ican3_msg *msg)
    +{
    + dev_warn(mod->dev, "recieved unknown message: spec 0x%.2x length %d\n",
    + msg->spec, le16_to_cpu(msg->len));
    +}
    +
    +/*
    + * Handle a control message from the firmware
    + */
    +static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
    +{
    + dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
    + mod->num, msg->spec, le16_to_cpu(msg->len));
    +
    + switch (msg->spec) {
    + case MSG_IDVERS:
    + ican3_handle_idvers(mod, msg);
    + break;
    + case MSG_MSGLOST:
    + case MSG_FMSGLOST:
    + ican3_handle_msglost(mod, msg);
    + break;
    + case MSG_CEVTIND:
    + ican3_handle_cevtind(mod, msg);
    + break;
    + default:
    + ican3_handle_unknown_message(mod, msg);
    + break;
    + }
    +}
    +
    +/*
    + * Check that there is room in the TX ring to transmit another skb
    + *
    + * LOCKING: must hold mod->lock
    + */
    +static bool ican3_txok(struct ican3_dev *mod)
    +{
    + struct ican3_fast_desc __iomem *desc;
    + u8 control;
    +
    + /* copy the control bits of the descriptor */
    + ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
    + desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc));
    + control = ioread8(&desc->control);
    +
    + /* if the control bits are not valid, then we have no more space */
    + if (!(control & DESC_VALID))
    + return false;
    +
    + return true;
    +}
    +
    +/*
    + * Recieve one CAN frame from the hardware
    + *
    + * This works like the core of a NAPI function, but is intended to be called
    + * from workqueue context instead. This driver already needs a workqueue to
    + * process control messages, so we use the workqueue instead of using NAPI.
    + * This was done to simplify locking.
    + *
    + * CONTEXT: must be called from user context
    + */
    +static int ican3_recv_skb(struct ican3_dev *mod)
    +{
    + struct net_device *ndev = mod->ndev;
    + struct net_device_stats *stats = &ndev->stats;
    + struct ican3_fast_desc desc;
    + void __iomem *desc_addr;
    + struct can_frame *cf;
    + struct sk_buff *skb;
    + unsigned long flags;
    +
    + spin_lock_irqsave(&mod->lock, flags);
    +
    + /* copy the whole descriptor */
    + ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
    + desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc));
    + memcpy_fromio(&desc, desc_addr, sizeof(desc));
    +
    + spin_unlock_irqrestore(&mod->lock, flags);
    +
    + /* check that we actually have a CAN frame */
    + if (!(desc.control & DESC_VALID))
    + return -ENOBUFS;
    +
    + /* allocate an skb */
    + skb = alloc_can_skb(ndev, &cf);
    + if (unlikely(skb == NULL)) {
    + stats->rx_dropped++;
    + goto err_noalloc;
    + }
    +
    + /* convert the ICAN3 frame into Linux CAN format */
    + ican3_to_can_frame(mod, &desc, cf);
    +
    + /* receive the skb, update statistics */
    + netif_receive_skb(skb);
    + stats->rx_packets++;
    + stats->rx_bytes += cf->can_dlc;
    +
    +err_noalloc:
    + /* toggle the valid bit and return the descriptor to the ring */
    + desc.control ^= DESC_VALID;
    +
    + spin_lock_irqsave(&mod->lock, flags);
    +
    + ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
    + memcpy_toio(desc_addr, &desc, 1);
    +
    + /* update the next buffer pointer */
    + mod->fastrx_num = (desc.control & DESC_WRAP) ? 0
    + : (mod->fastrx_num + 1);
    +
    + /* there are still more buffers to process */
    + spin_unlock_irqrestore(&mod->lock, flags);
    + return 0;
    +}
    +
    +static int ican3_napi(struct napi_struct *napi, int budget)
    +{
    + struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi);
    + struct ican3_msg msg;
    + unsigned long flags;
    + int received = 0;
    + int ret;
    +
    + /* process all communication messages */
    + while (true) {
    + ret = ican3_recv_msg(mod, &msg);
    + if (ret)
    + break;
    +
    + ican3_handle_message(mod, &msg);
    + }
    +
    + /* process all CAN frames from the fast interface */
    + while (received < budget) {
    + ret = ican3_recv_skb(mod);
    + if (ret)
    + break;
    +
    + received++;
    + }
    +
    + /* We have processed all packets that the adapter had, but it
    + * was less than our budget, stop polling */
    + if (received < budget)
    + napi_complete(napi);
    +
    + spin_lock_irqsave(&mod->lock, flags);
    +
    + /* Wake up the transmit queue if necessary */
    + if (netif_queue_stopped(mod->ndev) && ican3_txok(mod))
    + netif_wake_queue(mod->ndev);
    +
    + spin_unlock_irqrestore(&mod->lock, flags);
    +
    + /* re-enable interrupt generation */
    + iowrite8(1 << mod->num, &mod->ctrl->int_enable);
    + return received;
    +}
    +
    +static irqreturn_t ican3_irq(int irq, void *dev_id)
    +{
    + struct ican3_dev *mod = dev_id;
    + u8 stat;
    +
    + /*
    + * The interrupt status register on this device reports interrupts
    + * as zeroes instead of using ones like most other devices
    + */
    + stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
    + if (stat == (1 << mod->num))
    + return IRQ_NONE;
    +
    + dev_dbg(mod->dev, "IRQ: module %d\n", mod->num);
    +
    + /* clear the MODULbus interrupt from the microcontroller */
    + ioread8(&mod->dpmctrl->interrupt);
    +
    + /* disable interrupt generation, schedule the NAPI poller */
    + iowrite8(1 << mod->num, &mod->ctrl->int_disable);
    + napi_schedule(&mod->napi);
    + return IRQ_HANDLED;
    +}
    +
    +/*
    + * Firmware reset, startup, and shutdown
    + */
    +
    +/*
    + * Reset an ICAN module to its power-on state
    + *
    + * CONTEXT: no network device registered
    + * LOCKING: work function disabled
    + */
    +static int ican3_reset_module(struct ican3_dev *mod)
    +{
    + u8 val = 1 << mod->num;
    + unsigned long start;
    + u8 runold, runnew;
    +
    + /* disable interrupts so no more work is scheduled */
    + iowrite8(1 << mod->num, &mod->ctrl->int_disable);
    +
    + /* flush any pending work */
    + flush_scheduled_work();
    +
    + /* the first unallocated page in the DPM is #9 */
    + mod->free_page = DPM_FREE_START;
    +
    + ican3_set_page(mod, QUEUE_OLD_CONTROL);
    + runold = ioread8(mod->dpm + TARGET_RUNNING);
    +
    + /* reset the module */
    + iowrite8(val, &mod->ctrl->reset_assert);
    + iowrite8(val, &mod->ctrl->reset_deassert);
    +
    + /* wait until the module has finished resetting and is running */
    + start = jiffies;
    + do {
    + ican3_set_page(mod, QUEUE_OLD_CONTROL);
    + runnew = ioread8(mod->dpm + TARGET_RUNNING);
    + if (runnew == (runold ^ 0xff))
    + return 0;
    +
    + msleep(10);
    + } while (time_before(jiffies, start + HZ / 4));
    +
    + dev_err(mod->dev, "failed to reset CAN module\n");
    + return -ETIMEDOUT;
    +}
    +
    +static void __devexit ican3_shutdown_module(struct ican3_dev *mod)
    +{
    + ican3_msg_disconnect(mod);
    + ican3_reset_module(mod);
    +}
    +
    +/*
    + * Startup an ICAN module, bringing it into fast mode
    + */
    +static int __devinit ican3_startup_module(struct ican3_dev *mod)
    +{
    + int ret;
    +
    + ret = ican3_reset_module(mod);
    + if (ret) {
    + dev_err(mod->dev, "unable to reset module\n");
    + return ret;
    + }
    +
    + /* re-enable interrupts so we can send messages */
    + iowrite8(1 << mod->num, &mod->ctrl->int_enable);
    +
    + ret = ican3_msg_connect(mod);
    + if (ret) {
    + dev_err(mod->dev, "unable to connect to module\n");
    + return ret;
    + }
    +
    + ican3_init_new_host_interface(mod);
    + ret = ican3_msg_newhostif(mod);
    + if (ret) {
    + dev_err(mod->dev, "unable to switch to new-style interface\n");
    + return ret;
    + }
    +
    + ret = ican3_set_termination(mod, true);
    + if (ret) {
    + dev_err(mod->dev, "unable to enable termination\n");
    + return ret;
    + }
    +
    + ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX);
    + if (ret) {
    + dev_err(mod->dev, "unable to set bus-error\n");
    + return ret;
    + }
    +
    + ican3_init_fast_host_interface(mod);
    + ret = ican3_msg_fasthostif(mod);
    + if (ret) {
    + dev_err(mod->dev, "unable to switch to fast host interface\n");
    + return ret;
    + }
    +
    + ret = ican3_set_id_filter(mod, true);
    + if (ret) {
    + dev_err(mod->dev, "unable to set acceptance filter\n");
    + return ret;
    + }
    +
    + return 0;
    +}
    +
    +/*
    + * CAN Network Device
    + */
    +
    +static int ican3_open(struct net_device *ndev)
    +{
    + struct ican3_dev *mod = netdev_priv(ndev);
    + int ret;
    +
    + /* open the CAN layer */
    + ret = open_candev(ndev);
    + if (ret) {
    + dev_err(mod->dev, "unable to start CAN layer\n");
    + return ret;
    + }
    +
    + /* bring the bus online */
    + ret = ican3_set_bus_state(mod, true);
    + if (ret) {
    + dev_err(mod->dev, "unable to set bus-on\n");
    + close_candev(ndev);
    + return ret;
    + }
    +
    + /* start up the network device */
    + mod->can.state = CAN_STATE_ERROR_ACTIVE;
    + netif_start_queue(ndev);
    +
    + return 0;
    +}
    +
    +static int ican3_stop(struct net_device *ndev)
    +{
    + struct ican3_dev *mod = netdev_priv(ndev);
    + int ret;
    +
    + /* stop the network device xmit routine */
    + netif_stop_queue(ndev);
    + mod->can.state = CAN_STATE_STOPPED;
    +
    + /* bring the bus offline, stop receiving packets */
    + ret = ican3_set_bus_state(mod, false);
    + if (ret) {
    + dev_err(mod->dev, "unable to set bus-off\n");
    + return ret;
    + }
    +
    + /* close the CAN layer */
    + close_candev(ndev);
    + return 0;
    +}
    +
    +static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
    +{
    + struct ican3_dev *mod = netdev_priv(ndev);
    + struct net_device_stats *stats = &ndev->stats;
    + struct can_frame *cf = (struct can_frame *)skb->data;
    + struct ican3_fast_desc desc;
    + void __iomem *desc_addr;
    + unsigned long flags;
    +
    + spin_lock_irqsave(&mod->lock, flags);
    +
    + /* check that we can actually transmit */
    + if (!ican3_txok(mod)) {
    + dev_err(mod->dev, "no free descriptors, stopping queue\n");
    + netif_stop_queue(ndev);
    + spin_unlock_irqrestore(&mod->lock, flags);
    + return NETDEV_TX_BUSY;
    + }
    +
    + /* copy the control bits of the descriptor */
    + ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
    + desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc));
    + memset(&desc, 0, sizeof(desc));
    + memcpy_fromio(&desc, desc_addr, 1);
    +
    + /* convert the Linux CAN frame into ICAN3 format */
    + can_frame_to_ican3(mod, cf, &desc);
    +
    + /*
    + * the programming manual says that you must set the IVALID bit, then
    + * interrupt, then set the valid bit. Quite weird, but it seems to be
    + * required for this to work
    + */
    + desc.control |= DESC_IVALID;
    + memcpy_toio(desc_addr, &desc, sizeof(desc));
    +
    + /* generate a MODULbus interrupt to the microcontroller */
    + iowrite8(0x01, &mod->dpmctrl->interrupt);
    +
    + desc.control ^= DESC_VALID;
    + memcpy_toio(desc_addr, &desc, sizeof(desc));
    +
    + /* update the next buffer pointer */
    + mod->fasttx_num = (desc.control & DESC_WRAP) ? 0
    + : (mod->fasttx_num + 1);
    +
    + /* update statistics */
    + stats->tx_packets++;
    + stats->tx_bytes += cf->can_dlc;
    + kfree_skb(skb);
    +
    + /*
    + * This hardware doesn't have TX-done notifications, so we'll try and
    + * emulate it the best we can using ECHO skbs. Get the next TX
    + * descriptor, and see if we have room to send. If not, stop the queue.
    + * It will be woken when the ECHO skb for the current packet is recv'd.
    + */
    +
    + /* copy the control bits of the descriptor */
    + if (!ican3_txok(mod))
    + netif_stop_queue(ndev);
    +
    + spin_unlock_irqrestore(&mod->lock, flags);
    + return NETDEV_TX_OK;
    +}
    +
    +static const struct net_device_ops ican3_netdev_ops = {
    + .ndo_open = ican3_open,
    + .ndo_stop = ican3_stop,
    + .ndo_start_xmit = ican3_xmit,
    +};
    +
    +/*
    + * Low-level CAN Device
    + */
    +
    +/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */
    +static struct can_bittiming_const ican3_bittiming_const = {
    + .name = DRV_NAME,
    + .tseg1_min = 1,
    + .tseg1_max = 16,
    + .tseg2_min = 1,
    + .tseg2_max = 8,
    + .sjw_max = 4,
    + .brp_min = 1,
    + .brp_max = 64,
    + .brp_inc = 1,
    +};
    +
    +/*
    + * This routine was stolen from drivers/net/can/sja1000/sja1000.c
    + *
    + * The bittiming register command for the ICAN3 just sets the bit timing
    + * registers on the SJA1000 chip directly
    + */
    +static int ican3_set_bittiming(struct net_device *ndev)
    +{
    + struct ican3_dev *mod = netdev_priv(ndev);
    + struct can_bittiming *bt = &mod->can.bittiming;
    + struct ican3_msg msg;
    + u8 btr0, btr1;
    +
    + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
    + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
    + (((bt->phase_seg2 - 1) & 0x7) << 4);
    + if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
    + btr1 |= 0x80;
    +
    + memset(&msg, 0, sizeof(msg));
    + msg.spec = MSG_CBTRREQ;
    + msg.len = cpu_to_le16(4);
    + msg.data[0] = 0x00;
    + msg.data[1] = 0x00;
    + msg.data[2] = btr0;
    + msg.data[3] = btr1;
    +
    + return ican3_send_msg(mod, &msg);
    +}
    +
    +static int ican3_set_mode(struct net_device *ndev, enum can_mode mode)
    +{
    + struct ican3_dev *mod = netdev_priv(ndev);
    + int ret;
    +
    + if (mode != CAN_MODE_START)
    + return -ENOTSUPP;
    +
    + /* bring the bus online */
    + ret = ican3_set_bus_state(mod, true);
    + if (ret) {
    + dev_err(mod->dev, "unable to set bus-on\n");
    + return ret;
    + }
    +
    + /* start up the network device */
    + mod->can.state = CAN_STATE_ERROR_ACTIVE;
    +
    + if (netif_queue_stopped(ndev))
    + netif_wake_queue(ndev);
    +
    + return 0;
    +}
    +
    +/*
    + * PCI Subsystem
    + */
    +
    +static int __devinit ican3_probe(struct platform_device *pdev)
    +{
    + struct janz_platform_data *pdata;
    + struct net_device *ndev;
    + struct ican3_dev *mod;
    + struct resource *res;
    + struct device *dev;
    + int ret;
    +
    + pdata = pdev->dev.platform_data;
    + if (!pdata)
    + return -ENXIO;
    +
    + dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
    +
    + /* save the struct device for printing */
    + dev = &pdev->dev;
    +
    + /* allocate the CAN device and private data */
    + ndev = alloc_candev(sizeof(*mod), 0);
    + if (!ndev) {
    + dev_err(dev, "unable to allocate CANdev\n");
    + ret = -ENOMEM;
    + goto out_return;
    + }
    +
    + platform_set_drvdata(pdev, ndev);
    + mod = netdev_priv(ndev);
    + mod->ndev = ndev;
    + mod->dev = &pdev->dev;
    + mod->num = pdata->modno;
    + netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
    + spin_lock_init(&mod->lock);
    +
    + /* the first unallocated page in the DPM is 9 */
    + mod->free_page = DPM_FREE_START;
    +
    + ndev->netdev_ops = &ican3_netdev_ops;
    + ndev->flags |= IFF_ECHO;
    + SET_NETDEV_DEV(ndev, &pdev->dev);
    +
    + mod->can.clock.freq = 8000000;
    + mod->can.bittiming_const = &ican3_bittiming_const;
    + mod->can.do_set_bittiming = ican3_set_bittiming;
    + mod->can.do_set_mode = ican3_set_mode;
    +
    + /* find our IRQ number */
    + mod->irq = platform_get_irq(pdev, 0);
    + if (mod->irq < 0) {
    + dev_err(dev, "IRQ line not found\n");
    + ret = -ENODEV;
    + goto out_free_ndev;
    + }
    +
    + ndev->irq = mod->irq;
    +
    + /* get access to the MODULbus registers for this module */
    + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    + if (!res) {
    + dev_err(dev, "MODULbus registers not found\n");
    + ret = -ENODEV;
    + goto out_free_ndev;
    + }
    +
    + mod->dpm = ioremap(res->start, resource_size(res));
    + if (!mod->dpm) {
    + dev_err(dev, "MODULbus registers not ioremap\n");
    + ret = -ENOMEM;
    + goto out_free_ndev;
    + }
    +
    + mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE;
    +
    + /* get access to the control registers for this module */
    + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    + if (!res) {
    + dev_err(dev, "CONTROL registers not found\n");
    + ret = -ENODEV;
    + goto out_iounmap_dpm;
    + }
    +
    + mod->ctrl = ioremap(res->start, resource_size(res));
    + if (!mod->ctrl) {
    + dev_err(dev, "CONTROL registers not ioremap\n");
    + ret = -ENOMEM;
    + goto out_iounmap_dpm;
    + }
    +
    + /* disable our IRQ, then hookup the IRQ handler */
    + iowrite8(1 << mod->num, &mod->ctrl->int_disable);
    + ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod);
    + if (ret) {
    + dev_err(dev, "unable to request IRQ\n");
    + goto out_iounmap_ctrl;
    + }
    +
    + /* reset and initialize the CAN controller into fast mode */
    + napi_enable(&mod->napi);
    + ret = ican3_startup_module(mod);
    + if (ret) {
    + dev_err(dev, "%s: unable to start CANdev\n", __func__);
    + goto out_free_irq;
    + }
    +
    + /* register with the Linux CAN layer */
    + ret = register_candev(ndev);
    + if (ret) {
    + dev_err(dev, "%s: unable to register CANdev\n", __func__);
    + goto out_free_irq;
    + }
    +
    + dev_info(dev, "module %d: registered CAN device\n", pdata->modno);
    + return 0;
    +
    +out_free_irq:
    + napi_disable(&mod->napi);
    + iowrite8(1 << mod->num, &mod->ctrl->int_disable);
    + free_irq(mod->irq, mod);
    +out_iounmap_ctrl:
    + iounmap(mod->ctrl);
    +out_iounmap_dpm:
    + iounmap(mod->dpm);
    +out_free_ndev:
    + free_candev(ndev);
    +out_return:
    + return ret;
    +}
    +
    +static int __devexit ican3_remove(struct platform_device *pdev)
    +{
    + struct net_device *ndev = platform_get_drvdata(pdev);
    + struct ican3_dev *mod = netdev_priv(ndev);
    +
    + /* unregister the netdevice, stop interrupts */
    + unregister_netdev(ndev);
    + napi_disable(&mod->napi);
    + iowrite8(1 << mod->num, &mod->ctrl->int_disable);
    + free_irq(mod->irq, mod);
    +
    + /* put the module into reset */
    + ican3_shutdown_module(mod);
    +
    + /* unmap all registers */
    + iounmap(mod->ctrl);
    + iounmap(mod->dpm);
    +
    + free_candev(ndev);
    +
    + return 0;
    +}
    +
    +static struct platform_driver ican3_driver = {
    + .driver = {
    + .name = DRV_NAME,
    + .owner = THIS_MODULE,
    + },
    + .probe = ican3_probe,
    + .remove = __devexit_p(ican3_remove),
    +};
    +
    +static int __init ican3_init(void)
    +{
    + return platform_driver_register(&ican3_driver);
    +}
    +
    +static void __exit ican3_exit(void)
    +{
    + platform_driver_unregister(&ican3_driver);
    +}
    +
    +MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
    +MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver");
    +MODULE_LICENSE("GPL");
    +MODULE_ALIAS("platform:janz-ican3");
    +
    +module_init(ican3_init);
    +module_exit(ican3_exit);
    --
    1.5.4.3


    \
     
     \ /
      Last update: 2010-03-02 22:25    [W:0.417 / U:0.128 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site