Hello all,

I posted last week asking about a driver for boards running on a PLX
chip. It turns out that these are passive boards. I have been looking
for a driver for the Janz CMOD-IO board CAN interfaces.

I finally found the datasheets and took the time to write a driver. This
board is an intelligent CAN interface: it has onboard microprocessors to
help process CAN traffic.

This is a very rough first try at a CAN driver. I'm sure it still has
problems, and I would appreciate if you can take a look and help me add
what is needed. I'm am not extremely knowledgeable about the CAN bus.

The things that are known to be wrong:

- bus-on / bus-off handling

I did this straight in the network device open()/stop() methods. I don't
handle the condition where we get too many bus errors and the bus goes
into bus-off state. What should I be doing here?

- state changes, bit timing, etc.

I'm not at all sure how this is supposed to work. Perhaps someone that
knows CAN bus better that I do can help.

- CAN bus termination

This board supports optionally terminating the CAN bus. In order to test
this driver, I connected both CAN modules on a single board together. To
get this to work, I needed to turn on termination on both modules.

Is this wrong? Is there a way to enable/disable termination via the "ip"
tool?

- module probing

This board is really a MODULbus carrier board, into which plugs 4
daughterboards. These can be CAN modules, or others. On my board, I have
2x CAN modules and 1x TTL GPIO module. I need support for both of these
for my application. For the time being, I *did not* add support for the
TTL module. That will come once the CAN part is finished.

Also, there is no way I am aware of to determine what type of board is
plugged into which MODULbus slot on the carrier board. My CAN modules
support identification, but the TTL module does not.

I hard-coded the module layout into the driver itself. This is
sufficient for my purposes, but probably is not sufficient for mainline
Linux :'(. Any ideas or suggestions?


Any review will be much appreciated!

Thanks,
Ira


--------------------- cut here ----------------------


/*
 * Janz PCI CAN Driver
 *
 * Copyright (c) 2010 Ira W. Snyder <[email protected]>
 *
 * This file is licensed under the terms of the GNU General Public License
 * version 2. This program is licensed "as is" without any warranty of any
 * kind, whether express or implied.
 */

#define DEBUG 1

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>

#include <linux/netdevice.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>

static const char drv_name[] = "janz";

/* PLX bridge chip onboard registers */
#define JANZ_OB_INT_STAT        0x1             /* read  access */
#define JANZ_OB_INT_DISABLE     0x1             /* write access */
#define JANZ_OB_MBUS_NUM        0x3             /* read  access */
#define JANZ_OB_INT_ENABLE      0x3             /* write access */
#define JANZ_OB_RESET_ASSERT    0x5             /* write access */
#define JANZ_OB_RESET_DEASSERT  0x7             /* write access */
#define JANZ_OB_EEP             0x9             /* rw access */
#define JANZ_OB_ENID            0xb             /* write access */
/* 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 "old-style" host interface control registers */
#define MSYNC_PEER              0x00            /* ICAN only */
#define MSYNC_LOCL              0x01            /* host only */
#define TARGET_RUNNING          0x02

/* Janz "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

/* Janz "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 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

/* Number of buffers for use in the "new-style" host interface */
#define JANZ_NEW_BUFFERS 16

/* Number of buffers for use in the "fast" host interface */
#define JANZ_FAST_BUFFERS 256

/* Maximum number of buffers on a CMOD-IO carrier board */
#define JANZ_MAX_MODULES 4

/*
 * This is a terrible solution to an ugly problem
 *
 * The Janz CMOD-IO carrier board supports both MODULbus and MODULbus+
 * modules, however, only MODULbus+ modules are required to have a serial
 * EEPROM onboard for automatic identification.
 *
 * This means that it is impossible to detect which modules are actually
 * present on any one CMOD-IO board.
 *
 * I chose the solution that the user will have to change the defines below
 * to match their actual hardware. There isn't really anything else I can do.
 * This is sufficient for my purposes: all boards I own have the same
 * configuration.
 *
 * Janz's character driver does this setup via string module parameters,
 * which are fairly ugly to parse inside the kernel.
 */

/* Module Types */
#define JANZ_MODULE_NONE        0
#define JANZ_MODULE_TTL         1
#define JANZ_MODULE_ICAN3       2

/* Modules Present */
static const int janz_modules_present[JANZ_MAX_MODULES] = {
        JANZ_MODULE_ICAN3,
        JANZ_MODULE_ICAN3,
        JANZ_MODULE_NONE,
        JANZ_MODULE_TTL,
};

struct janz_module {

        /* must be the first member */
        struct can_priv can;

        /* CAN network device */
        struct net_device *ndev;
        struct napi_struct napi;

        /* parent PCI device */
        struct janz_device *parent;

        /* module number */
        unsigned int num;

        /* base address of registers */
        void __iomem *regs;

        /* 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;

        /* interrupt handling */
        struct work_struct work;
};

struct janz_device {
        struct device *dev;
        struct pci_dev *pdev;

        void __iomem *control_regs;
        void __iomem *modulbus_regs;
        void __iomem *onboard_regs;

        /* hex switch position */
        u8 hex;

        struct janz_module *modules[JANZ_MAX_MODULES];
};

struct janz_msg {
        u8 control;
        u8 spec;
        __le16 len;
        u8 data[252];
};

struct janz_new_desc {
        u8 control;
        u8 pointer;
};

struct janz_fast_desc {
        u8 control;
        u8 command;
        u8 data[14];
};

/*----------------------------------------------------------------------------*/
/* Dual Ported Memory (DPM) Access                                            */
/*----------------------------------------------------------------------------*/

/*
 * The DPM has a fixed size of 64K byte, organized into 256x 256 byte pages
 *
 * You have a 0x200 byte view into DPM memory, split into two windows:
 *
 * 0x0000 - 0x00ff: DPM memory page
 * 0x0100 - 0x01ff: DPM control registers
 *
 * You can switch the page shown in the first window by using the control
 * registers. They also have other uses, not described here.
 */

static u8 janz_dpm_read8(struct janz_module *mod, unsigned int addr)
{
        /* the DPM pages are only 256 bytes long */
        BUG_ON(addr >= DPM_PAGE_SIZE);
        return ioread8(mod->regs + addr);
}

static void janz_dpm_write8(struct janz_module *mod, unsigned int addr, u8 val)
{
        /* the DPM pages are only 256 bytes long */
        BUG_ON(addr >= DPM_PAGE_SIZE);
        iowrite8(val, mod->regs + addr);
}

/*----------------------------------------------------------------------------*/
/* DPM Register Access                                                        */
/*----------------------------------------------------------------------------*/

/* write to the window basic address register */
static void janz_set_page(struct janz_module *mod, unsigned int page)
{
        /* the DPM only has 256 pages */
        BUG_ON(page >= 256);
        iowrite8(page, mod->regs + 0x100);
}

/* clear a MODULbus interrupt */
static void janz_clr_int(struct janz_module *mod)
{
        ioread8(mod->regs + 0x102);
}

/* generate a MODULbus interrupt */
static void janz_set_int(struct janz_module *mod)
{
        iowrite8(0x01, mod->regs + 0x102);
}

#if 0
/* generate a hardware reset on the ICAN3 */
static void janz_set_hwreset(struct janz_module *mod)
{
        iowrite8(0x01, mod->regs + 0x104);
}

/* generate a TPU interrupt on the ICAN3 */
static void janz_set_tpuint(struct janz_module *mod)
{
        iowrite8(0x01, mod->regs + 0x106);
}
#endif

/*----------------------------------------------------------------------------*/
/* Onboard Registers                                                          */
/*----------------------------------------------------------------------------*/

static void janz_disable_interrupts(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        iowrite8(1 << mod->num, priv->onboard_regs + JANZ_OB_INT_DISABLE);
}

static void janz_enable_interrupts(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        iowrite8(1 << mod->num, priv->onboard_regs + JANZ_OB_INT_ENABLE);
}

#if 0

static inline void eep(struct janz_device *priv, u8 eck, u8 edio)
{
        u8 d = ((eck << 1) | (edio & 1)) ^ 0x03;
        iowrite8(d, priv->onboard_regs + JANZ_OB_EEP);
}

static inline void selhi(struct janz_device *priv, unsigned int mod)
{
        iowrite8(1 << mod, priv->onboard_regs + JANZ_OB_ENID);
}

static inline void selnone(struct janz_device *priv)
{
        iowrite8(0x00, priv->onboard_regs + JANZ_OB_ENID);
}

/*
 * Read a MODULbus+ EEPROM on a module
 *
 * This requires generating waveforms in software, which is pretty
 * uncomfortable and unfriendly. For reasons stated above, this
 * is a useless feature anyway, since it doesn't work on non-MODULbus+
 * modules
 */
static void janz_read_module_id(struct janz_device *priv, unsigned int mod)
{
        int i, cell;
        u16 val;
        u8 bit;

        /
        /*
         * This requires that you generate EEPROM waveforms in software, making
         * it pretty much impractical. You need to use the ENID register to
         * enable the module id lines, and then use the EEP register to
         * generate the waveforms for the EEPROM. Painful.
         */

        /* put the bus in an idle state */
        selnone(priv);
        eep(priv, 0, 1);

        /* Chip Select the correct module */
        selhi(priv, mod);

        /* generate the start bit (data == 1) */
        eep(priv, 1, 1);
        eep(priv, 0, 1);

        /* opcode 10 */
        eep(priv, 1, 1);
        eep(priv, 0, 1);

        eep(priv, 1, 0);
        eep(priv, 0, 0);

        /* 6 address bits, all 0 */
        eep(priv, 1, 0);
        eep(priv, 0, 0);

        eep(priv, 1, 0);
        eep(priv, 0, 0);

        eep(priv, 1, 0);
        eep(priv, 0, 0);

        eep(priv, 1, 0);
        eep(priv, 0, 0);

        eep(priv, 1, 0);
        eep(priv, 0, 0);

        eep(priv, 1, 0);
        eep(priv, 0, 0);

        /* tri-state the data line */
        eep(priv, 0, 1);

        for (cell = 0; cell < 64; cell++) {

                /* read 16 bits at a time */
                val = 0;
                for (i = 0; i < 16; i++) {
                        eep(priv, 1, 1);
                        eep(priv, 0, 1);
                        bit = (ioread8(priv->onboard_regs + JANZ_OB_EEP) & 
0x01) ^ 0x01;
                        val |= (bit << (15 - i));
                }

                dev_dbg(priv->dev, "%s: DATA16 CELL %d -> %.4x\n", __func__, 
cell, val);
        }

        /* de-assert chip select, reset the data and clock lines */
        selnone(priv);
        eep(priv, 1, 1);
}
#endif

/*----------------------------------------------------------------------------*/
/* Janz "old-style" host interface                                            */
/*----------------------------------------------------------------------------*/

/*
 * Get the MSYNC bits from the "old-style" interface control
 * registers
 */
static void janz_get_msync(struct janz_module *mod, u8 *locl, u8 *peer)
{
        janz_set_page(mod, 0);
        *peer = janz_dpm_read8(mod, MSYNC_PEER);
        *locl = janz_dpm_read8(mod, MSYNC_LOCL);
}

/*
 * Recieve a message from the Janz "old-style" firmware interface
 *
 * @priv: device private data
 * @mod: MODULbus module number
 * @msg: message buffer storage
 * @return: 0 on success, -ENOMEM when no message exists
 */
static int janz_old_recv_msg(struct janz_module *mod, struct janz_msg *msg)
{
        struct janz_device *priv = mod->parent;
        u8 locl, peer, xord;
        unsigned int mbox;

        /* get the MSYNC registers */
        janz_get_msync(mod, &locl, &peer);
        xord = locl ^ peer;

        if ((xord & 0x03) == 0x00) {
                dev_dbg(priv->dev, "no mbox for reading\n");
                return -ENOMEM;
        }

        /* find the first free mbox to read */
        if ((xord & 0x03) == 0x03)
                mbox = (xord & 0x04) ? 0 : 1;
        else
                mbox = (xord & 0x01) ? 0 : 1;

        /* copy the message */
        janz_set_page(mod, mbox + 1);
        memcpy_fromio(msg, mod->regs, sizeof(*msg));

        /*
         * notify the firmware that the read buffer is available
         * for it to fill again
         */
        locl ^= (1 << mbox);

        janz_set_page(mod, 0);
        janz_dpm_write8(mod, MSYNC_LOCL, locl);
        return 0;
}

/*
 * Send a message through the "old-style" firmware interface
 *
 * @priv: device private data
 * @mod: MODULbus module number
 * @msg: message buffer storage
 * @return: 0 on success, -ENOMEM when no free space exists
 */
static int janz_old_send_msg(struct janz_module *mod, struct janz_msg *msg)
{
        struct janz_device *priv = mod->parent;
        u8 locl, peer, xord;
        unsigned int mbox;

        /* get the MSYNC registers */
        janz_get_msync(mod, &locl, &peer);
        xord = locl ^ peer;

        if ((xord & 0x30) == 0x30) {
                dev_err(priv->dev, "no mbox for writing\n");
                return -ENOMEM;
        }

        /* calculate a free mbox to use */
        mbox = (xord & 0x10) ? 1 : 0;

        /* copy the message to the DPM */
        janz_set_page(mod, mbox + 3);
        memcpy_toio(mod->regs, msg, sizeof(*msg));

        locl ^= (mbox == 0) ? 0x10 : 0x20;
        locl |= (mbox == 0) ? 0x00 : 0x40;

        janz_set_page(mod, 0);
        janz_dpm_write8(mod, MSYNC_LOCL, locl);
        return 0;
}

/*----------------------------------------------------------------------------*/
/* Janz "new-style" Host Interface Setup                                      */
/*----------------------------------------------------------------------------*/

static void janz_init_new_host_interface(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        struct janz_new_desc desc;
        unsigned long flags;
        void __iomem *dst;
        int i;

        spin_lock_irqsave(&mod->lock, flags);

        dev_dbg(priv->dev, "%s: starting at page %d\n", __func__, 
mod->free_page);

        /* setup the internal datastructures for RX */
        mod->rx_num = 0;
        mod->rx_int = 0;

        /* tohost queue descriptors are in page 5 */
        janz_set_page(mod, 5);
        dst = mod->regs;

        /* initialize the tohost (rx) queue descriptors: pages 9-24 */
        for (i = 0; i < JANZ_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 == JANZ_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 */
        janz_set_page(mod, 6);
        dst = mod->regs;

        /* setup the internal datastructures for TX */
        mod->tx_num = 0;

        /* initialize the fromhost mid queue descriptors: pages 25-40 */
        for (i = 0; i < JANZ_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 == JANZ_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 */
        janz_set_page(mod, 7);
        dst = mod->regs;

        /* 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 */
        janz_set_page(mod, 8);
        dst = mod->regs;

        /* 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++;

        dev_dbg(priv->dev, "%s: next free page %d\n", __func__, mod->free_page);
        spin_unlock_irqrestore(&mod->lock, flags);
}

/*----------------------------------------------------------------------------*/
/* Janz Fast Host Interface Setup                                             */
/*----------------------------------------------------------------------------*/

static void janz_init_fast_host_interface(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        struct janz_fast_desc desc;
        unsigned long flags;
        unsigned int addr;
        void __iomem *dst;
        int i;

        spin_lock_irqsave(&mod->lock, flags);
        dev_dbg(priv->dev, "%s: starting at page %d\n", __func__, 
mod->free_page);

        /* 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 < JANZ_FAST_BUFFERS; i++) {

                /* set the wrap bit on the last buffer */
                if (i == JANZ_FAST_BUFFERS - 1)
                        desc.control |= DESC_WRAP;

                /* switch to the correct page */
                janz_set_page(mod, mod->free_page);

                /* copy the descriptor to the DPM */
                dst = mod->regs + addr;
                memcpy_toio(dst, &desc, sizeof(desc));
                addr += sizeof(desc);

                /* move to the next page if necessary */
                if (addr >= 256) {
                        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 < JANZ_FAST_BUFFERS; i++) {

                /* set the wrap bit on the last buffer */
                if (i == JANZ_FAST_BUFFERS - 1)
                        desc.control |= DESC_WRAP;

                /* switch to the correct page */
                janz_set_page(mod, mod->free_page);

                /* copy the descriptor to the DPM */
                dst = mod->regs + addr;
                memcpy_toio(dst, &desc, sizeof(desc));
                addr += sizeof(desc);

                /* move to the next page if necessary */
                if (addr >= 256) {
                        addr = 0;
                        mod->free_page++;
                }
        }

        dev_dbg(priv->dev, "%s: next free page %d\n", __func__, mod->free_page);
        spin_unlock_irqrestore(&mod->lock, flags);
}

/*----------------------------------------------------------------------------*/
/* Janz "new-style" Host Interface Message Helpers                            */
/*----------------------------------------------------------------------------*/

/*
 * LOCKING: must hold mod->lock
 */
static int janz_new_send_msg(struct janz_module *mod, struct janz_msg *msg)
{
        struct janz_new_desc desc;
        struct janz_device *priv = mod->parent;
        void __iomem *desc_addr = mod->regs + (mod->tx_num * sizeof(desc));

        /* switch to the fromhost mid queue, and read the buffer descriptor */
        janz_set_page(mod, 6);
        memcpy_fromio(&desc, desc_addr, sizeof(desc));

        if (!(desc.control & DESC_VALID)) {
                dev_dbg(priv->dev, "%s: no free buffers\n", __func__);
                return -ENOMEM;
        }

        /* switch to the data page, copy the data */
        janz_set_page(mod, desc.pointer);
        memcpy_toio(mod->regs, msg, sizeof(*msg));

        /* switch back to the descriptor, set the valid bit, write it back */
        janz_set_page(mod, 6);
        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);
        dev_dbg(priv->dev, "%s: update TX num -> %d\n", __func__, mod->tx_num);

        return 0;
}

/*
 * LOCKING: must hold mod->lock
 */
static int janz_new_recv_msg(struct janz_module *mod, struct janz_msg *msg)
{
        struct janz_new_desc desc;
        struct janz_device *priv = mod->parent;
        void __iomem *desc_addr = mod->regs + (mod->rx_num * sizeof(desc));

        /* switch to the tohost queue, and read the buffer descriptor */
        janz_set_page(mod, 5);
        memcpy_fromio(&desc, desc_addr, sizeof(desc));

        if (!(desc.control & DESC_VALID)) {
                dev_dbg(priv->dev, "%s: no buffers to recv\n", __func__);
                return -ENOMEM;
        }

        /* switch to the data page, copy the data */
        janz_set_page(mod, desc.pointer);
        memcpy_fromio(msg, mod->regs, sizeof(*msg));

        /* switch back to the descriptor, toggle the valid bit, write it back */
        janz_set_page(mod, 5);
        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);
        dev_dbg(priv->dev, "%s: update RX num -> %d\n", __func__, mod->rx_num);

        return 0;
}

/*----------------------------------------------------------------------------*/
/* Message Send / Recv Helpers                                                */
/*----------------------------------------------------------------------------*/

static int janz_send_msg(struct janz_module *mod, struct janz_msg *msg)
{
        unsigned long flags;
        int ret;

        spin_lock_irqsave(&mod->lock, flags);

        if (mod->iftype == 0)
                ret = janz_old_send_msg(mod, msg);
        else
                ret = janz_new_send_msg(mod, msg);

        spin_unlock_irqrestore(&mod->lock, flags);
        return ret;
}

static int janz_recv_msg(struct janz_module *mod, struct janz_msg *msg)
{
        unsigned long flags;
        int ret;

        spin_lock_irqsave(&mod->lock, flags);

        if (mod->iftype == 0)
                ret = janz_old_recv_msg(mod, msg);
        else
                ret = janz_new_recv_msg(mod, msg);

        spin_unlock_irqrestore(&mod->lock, flags);
        return ret;
}

/*----------------------------------------------------------------------------*/
/* Quick Pre-constructed Messages                                             */
/*----------------------------------------------------------------------------*/

static int janz_msg_connect(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        struct janz_msg msg;
        int ret;

        memset(&msg, 0, sizeof(msg));
        msg.control = 0x00;
        msg.spec    = MSG_CONNECTI;
        msg.len     = cpu_to_le16(0);

        ret = janz_send_msg(mod, &msg);
        if (ret) {
                dev_dbg(priv->dev, "unable to send CONNECT message\n");
                return ret;
        }

        return 0;
}

static int janz_msg_disconnect(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        struct janz_msg msg;
        int ret;

        memset(&msg, 0, sizeof(msg));
        msg.control = 0x00;
        msg.spec    = MSG_DISCONNECT;
        msg.len     = cpu_to_le16(0);

        ret = janz_send_msg(mod, &msg);
        if (ret) {
                dev_dbg(priv->dev, "unable to send DISCONNECT message\n");
                return ret;
        }

        return 0;
}

static int janz_msg_newhostif(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        struct janz_msg msg;
        int ret;

        memset(&msg, 0, sizeof(msg));
        msg.control = 0x00;
        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 = janz_send_msg(mod, &msg);
        if (ret) {
                dev_dbg(priv->dev, "unable to send NEWHOSTIF message\n");
                return ret;
        }

        /* mark the module as using the new host interface */
        mod->iftype = 1;
        return 0;
}

static int janz_msg_fasthostif(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        struct janz_msg msg;
        unsigned int addr;
        int ret;

        memset(&msg, 0, sizeof(msg));
        msg.control = 0x00;
        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);

        ret = janz_send_msg(mod, &msg);
        if (ret) {
                dev_dbg(priv->dev, "unable to send FASTHOSTIF message\n");
                return ret;
        }

        return 0;
}

/*
 * Setup the CAN filter to either accept or reject all
 * messages from the CAN bus.
 */
static int janz_set_id_filter(struct janz_module *mod, bool accept)
{
        struct janz_device *priv = mod->parent;
        struct janz_msg msg;
        int ret;

        /* Standard Frame Format */
        memset(&msg, 0, sizeof(msg));
        msg.control = 0x00;
        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 ? 0x02 : 0x00;

        ret = janz_send_msg(mod, &msg);
        if (ret) {
                dev_dbg(priv->dev, "unable to send SETAFILMASK message\n");
                return ret;
        }

        /* Extended Frame Format */
        memset(&msg, 0, sizeof(msg));
        msg.control = 0x00;
        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 ? 0x02 : 0x00;

        ret = janz_send_msg(mod, &msg);
        if (ret) {
                dev_dbg(priv->dev, "unable to send SETAFILMASK message\n");
                return ret;
        }

        return 0;
}

/*
 * Bring the CAN bus online or offline
 */
static int janz_set_bus_state(struct janz_module *mod, bool on)
{
        struct janz_device *priv = mod->parent;
        struct janz_msg msg;
        int ret;

        memset(&msg, 0, sizeof(msg));
        msg.control = 0x00;
        msg.spec    = on ? MSG_CONREQ : MSG_COFFREQ;
        msg.len     = cpu_to_le16(0);

        dev_dbg(priv->dev, "%s: %s request: spec %.2x\n", __func__, on ? "on" : 
"off", msg.spec);
        ret = janz_send_msg(mod, &msg);
        if (ret) {
                dev_dbg(priv->dev, "unable to send CONREQ/COFFREQ message\n");
                return ret;
        }

        return 0;
}

static int janz_set_termination(struct janz_module *mod, bool on)
{
        struct janz_device *priv = mod->parent;
        struct janz_msg msg;
        int ret;

        memset(&msg, 0, sizeof(msg));
        msg.control = 0x00;
        msg.spec    = MSG_HWCONF;
        msg.len     = cpu_to_le16(2);
        msg.data[0] = 0x00;
        msg.data[1] = on ? 0x01 : 0x00;

        ret = janz_send_msg(mod, &msg);
        if (ret) {
                dev_dbg(priv->dev, "unable to send HWCONF message\n");
                return ret;
        }

        return 0;
}

/*----------------------------------------------------------------------------*/
/* Interrupt Handling                                                         */
/*----------------------------------------------------------------------------*/

static void janz_handle_idvers(struct janz_module *mod, struct janz_msg *msg)
{
        struct janz_device *priv = mod->parent;

        dev_dbg(priv->dev, "%s: %s\n", __func__, msg->data);
}

static void janz_handle_msglost(struct janz_module *mod, struct janz_msg *msg)
{
        struct janz_device *priv = mod->parent;
        char *queue;

        if (msg->spec == MSG_MSGLOST)
                queue = "new";
        else
                queue = "fast";

        dev_dbg(priv->dev, "%s: %s hostif: %d messages lost\n",
                           __func__, queue, msg->data[0]);
}

static void janz_handle_cevtind(struct janz_module *mod, struct janz_msg *msg)
{
        struct janz_device *priv = mod->parent;
        char *s;
        int i;

        dev_dbg(priv->dev, "%s: message len: %d\n", __func__, 
le16_to_cpu(msg->len));
        for (i = 0; i < le16_to_cpu(msg->len); i++)
                dev_dbg(priv->dev, "%s: data[%.2d] -> %.2x\n", __func__, i, 
msg->data[i]);

        switch (msg->data[0]) {
        case 0x01:
                s = "error interrupt occurred";
                break;
        case 0x02:
                s = "overrun interrupt occurred";
                break;
        case 0x04:
                s = "interrupts lost";
                break;
        case 0x08:
                s = "send queue full";
                break;
        case 0x10:
                s = "CANbus bus-error";
                break;
        default:
                s = "unknown error";
                break;
        }

        dev_dbg(priv->dev, "%s: %s\n", __func__, s);
}

static void janz_handle_unknown(struct janz_module *mod, struct janz_msg *msg)
{
        struct janz_device *priv = mod->parent;
        u16 len;
        int i;

        len = le16_to_cpu(msg->len);
        dev_dbg(priv->dev, "%s: modno %d UNKNOWN spec 0x%.2x len %d\n",
                           __func__, mod->num, msg->spec, len);
        for (i = 0; i < len; i++)
                dev_dbg(priv->dev, "msg->data[%.2d] -> 0x%.2x\n", i, 
msg->data[i]);
}

static void janz_handle_message(struct janz_module *mod, struct janz_msg *msg)
{
        struct janz_device *priv = mod->parent;

        dev_dbg(priv->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:
                janz_handle_idvers(mod, msg);
                break;
        case MSG_MSGLOST:
        case MSG_FMSGLOST:
                janz_handle_msglost(mod, msg);
                break;
        case MSG_CEVTIND:
                janz_handle_cevtind(mod, msg);
                break;
        default:
                janz_handle_unknown(mod, msg);
                break;
        }
}

static void janz_work(struct work_struct *work)
{
        struct janz_module *mod = container_of(work, struct janz_module, work);
        struct janz_device *priv = mod->parent;
        unsigned int handled = 0;
        struct janz_msg msg;
        int ret;

        dev_dbg(priv->dev, "%s: module number %d\n", __func__, mod->num);

        /* process all communication messages */
        while (true) {

                ret = janz_recv_msg(mod, &msg);
                if (ret) {
                        dev_dbg(priv->dev, "%s: no more messages\n", __func__);
                        break;
                }

                janz_handle_message(mod, &msg);
                handled++;
        }

        dev_dbg(priv->dev, "%s: handled %d messages\n", __func__, handled);
}

/*
 * Handle a MODULbus interrupt
 *
 * Due to the way the firmware works, we must first go through all of the
 * buffers and unset their IVALID flag, then notify our NAPI poller or
 * work function to go ahead and process the message. The IVALID flag must
 * be unset before clearing the interrupt.
 *
 * Only after the message has been processed can the VALID flag be unset.
 */
static void janz_handle_interrupt(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        unsigned long flags;
        void __iomem *addr;
        u8 control;

        spin_lock_irqsave(&mod->lock, flags);

        /*
         * If we're using the old-style host interface, we only need to
         * start the work function, since the fast host interface (and
         * therefore CAN frame reception) cannot be working yet
         */
        if (mod->iftype == 0) {
                dev_dbg(priv->dev, "%s: old style host interface\n", __func__);
                schedule_work(&mod->work);
                goto out_unlock;
        }

        /*
         * Ok, at least the new-style host interface must be running, so we
         * need to go through it's buffers and unset all of their DESC_IVALID
         * bits before clearing the interrupt
         */
        while (true) {

                dev_dbg(priv->dev, "%s: modno %d new style host interface\n", 
__func__, mod->num);

                /* check the new host interface tohost queue */
                janz_set_page(mod, 5);
                addr = mod->regs + (mod->rx_int * sizeof(struct janz_new_desc));
                control = ioread8(addr);

                /* check if we're finished with buffers */
                if (!(control & DESC_IVALID))
                        break;

                /* write the control bits back with IVALID unset */
                control &= ~DESC_IVALID;
                iowrite8(control, addr);

                /*
                 * update the interrupt handler's position and schedule
                 * the work function to run at some point in the future
                 */
                mod->rx_int = (control & DESC_WRAP) ? 0 : (mod->rx_int + 1);
                schedule_work(&mod->work);
        }

        /* Check the fast host interface for interrupts */
        while (true) {

                dev_dbg(priv->dev, "%s: modno %d fast interface\n", __func__, 
mod->num);

                /* check the fast host interface */
                janz_set_page(mod, mod->fastrx_start + (mod->fastrx_int / 16));
                addr = mod->regs + ((mod->fastrx_int % 16) * sizeof(struct 
janz_fast_desc));
                control = ioread8(addr);

                /* check if we're finished with buffers */
                if (!(control & DESC_IVALID))
                        break;

                /* write back the control bits with IVALID unset */
                control &= ~DESC_IVALID;
                iowrite8(control, addr);

                /*
                 * update the interrupt handler's position and schedule
                 * the NAPI poller to run at some point in the future
                 */
                mod->fastrx_int = (control & DESC_WRAP) ? 0 : (mod->fastrx_int 
+ 1);
                napi_schedule(&mod->napi);
        }

out_unlock:
        janz_clr_int(mod);
        spin_unlock_irqrestore(&mod->lock, flags);
}

static irqreturn_t janz_irq(int irq, void *dev_id)
{
        struct janz_device *priv = dev_id;
        struct janz_module *mod;
        u8 stat;
        int i;

        /*
         * The interrupt status register on this device reports interrupts
         * as zeroes instead of using ones like most other devices
         */
        stat = ioread8(priv->onboard_regs + JANZ_OB_INT_STAT) & 0x0f;
        dev_dbg(priv->dev, "IRQ: enter stat 0x%.2x\n", stat);

        if (stat == 0x0f) {
                dev_dbg(priv->dev, "IRQ: none pending\n");
                return IRQ_NONE;
        }

        /* Figure out which module interrupted, and run its work function */
        for (i = 0; i < JANZ_MAX_MODULES; i++) {
                mod = priv->modules[i];
                if ((stat & (1 << i)) == 0x00) {
                        dev_dbg(priv->dev, "IRQ: module %d\n", i);
                        janz_handle_interrupt(mod);
                }
        }

        stat = ioread8(priv->onboard_regs + JANZ_OB_INT_STAT) & 0x0f;
        dev_dbg(priv->dev, "IRQ: exit stat 0x%.2x\n", stat);

        return IRQ_HANDLED;
}

/*----------------------------------------------------------------------------*/
/* TEST CODE                                                                  */
/*----------------------------------------------------------------------------*/

/*
 * Initialize the data structure for a module
 */
static void janz_init_module(struct janz_module *mod)
{
        mod->iftype = 0;
        mod->rx_int = 0;
        mod->rx_num = 0;
        mod->tx_num = 0;

        mod->fastrx_start = 0;
        mod->fastrx_int   = 0;
        mod->fastrx_num   = 0;
        mod->fasttx_start = 0;
        mod->fasttx_num   = 0;

        /* the first unallocated page in the DPM is 9 */
        mod->free_page = 9;
}

/*
 * Reset an ICAN module to its power-on state
 *
 * CONTEXT: no network device registered
 * LOCKING: napi + work disabled
 */
static int janz_reset_module(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        u8 val = 1 << mod->num;
        unsigned long start;
        u8 runold, runnew;

        /* disable interrupts so no more work is scheduled */
        janz_disable_interrupts(mod);

        /* flush any pending work */
        flush_scheduled_work();

        /* re-initialize the software state */
        janz_init_module(mod);

        janz_set_page(mod, 0);
        runold = janz_dpm_read8(mod, TARGET_RUNNING);

        /* reset the module */
        iowrite8(val, priv->onboard_regs + JANZ_OB_RESET_ASSERT);
        iowrite8(val, priv->onboard_regs + JANZ_OB_RESET_DEASSERT);

        /* wait until the module has finished resetting and is running */
        start = jiffies;
        do {
                janz_set_page(mod, 0);
                runnew = janz_dpm_read8(mod, TARGET_RUNNING);
                if (runnew == (runold ^ 0xff)) {
                        dev_dbg(priv->dev, "%s: success\n", __func__);
                        return 0;
                }

                dev_dbg(priv->dev, "%s: msleep(10)\n", __func__);
                msleep(10);
        } while (time_before(jiffies, start + HZ / 4));

        dev_dbg(priv->dev, "%s: timed out\n", __func__);
        return -ETIMEDOUT;
}

static void janz_shutdown_module(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        int ret;

        dev_dbg(priv->dev, "%s: disconnect and reset module\n", __func__);
        janz_msg_disconnect(mod);
        ret = janz_reset_module(mod);
        if (ret)
                dev_err(priv->dev, "unable to reset module\n");
}

/*
 * Startup an ICAN module, bringing it into fast mode
 */
static int janz_startup_module(struct janz_module *mod)
{
        struct janz_device *priv = mod->parent;
        int ret;

        dev_dbg(priv->dev, "%s: reset module\n", __func__);
        ret = janz_reset_module(mod);
        if (ret) {
                dev_err(priv->dev, "unable to reset module\n");
                return ret;
        }

        /* re-enable interrupts so we can send messages */
        janz_enable_interrupts(mod);

        dev_dbg(priv->dev, "%s: connect and switch to new if\n", __func__);
        ret = janz_msg_connect(mod);
        if (ret) {
                dev_err(priv->dev, "unable to connect to module\n");
                return ret;
        }

        janz_init_new_host_interface(mod);
        ret = janz_msg_newhostif(mod);
        if (ret) {
                dev_err(priv->dev, "unable to switch to new-style interface\n");
                return ret;
        }

        dev_dbg(priv->dev, "%s: enable termination\n", __func__);
        ret = janz_set_termination(mod, true);
        if (ret) {
                dev_err(priv->dev, "unable to enable termination\n");
                return ret;
        }

        dev_dbg(priv->dev, "%s: start fast host if\n", __func__);
        janz_init_fast_host_interface(mod);
        ret = janz_msg_fasthostif(mod);
        if (ret) {
                dev_err(priv->dev, "unable to switch to fast host interface\n");
                return ret;
        }

        dev_dbg(priv->dev, "%s: set filter to accept everything\n", __func__);
        ret = janz_set_id_filter(mod, true);
        if (ret) {
                dev_err(priv->dev, "unable to set acceptance filter\n");
                return ret;
        }

        return 0;
}

/*----------------------------------------------------------------------------*/
/* Janz to CAN Frame Conversion                                               */
/*----------------------------------------------------------------------------*/

static void janz_to_can(struct janz_module *mod, struct janz_fast_desc *desc,
                        struct can_frame *cf)
{
        struct janz_device *priv = mod->parent;

        if ((desc->command & 0x0f) == 0) {
                dev_dbg(priv->dev, "%s: old frame format\n", __func__);
                if (desc->data[1] & 0x10)
                        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] & 0x0f;
                memcpy(cf->data, &desc->data[2], sizeof(cf->data));
        } else {
                dev_dbg(priv->dev, "%s: new frame format\n", __func__);
                cf->can_dlc = desc->data[0] & 0x0f;
                if (desc->data[0] & 0x40)
                        cf->can_id |= CAN_RTR_FLAG;

                if (desc->data[0] & 0x80) {
                        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_to_janz(struct janz_module *mod, struct can_frame *cf,
                        struct janz_fast_desc *desc)
{
        struct janz_device *priv = mod->parent;

        /* 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 = 1;
        desc->data[0] |= cf->can_dlc;
        desc->data[1] |= 0x10; /* echo */

        if (cf->can_id & CAN_RTR_FLAG)
                desc->data[0] |= 0x40;

        /* pack the id into the correct places */
        if (cf->can_id & CAN_EFF_FLAG) {
                dev_dbg(priv->dev, "%s: extended frame\n", __func__);
                desc->data[0] |= 0x80; /* extended id frame */
                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(priv->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));
}

/*----------------------------------------------------------------------------*/
/* CAN Network Device                                                         */
/*----------------------------------------------------------------------------*/

static int janz_open(struct net_device *ndev)
{
        struct janz_module *mod = netdev_priv(ndev);
        struct janz_device *priv = mod->parent;
        int ret;

        dev_dbg(priv->dev, "%s: called\n", __func__);

        /* bring the bus online */
        ret = janz_set_bus_state(mod, true);
        if (ret) {
                dev_err(priv->dev, "unable to set bus-on\n");
                return ret;
        }

        /* start up the network device */
        napi_enable(&mod->napi);
        netif_start_queue(ndev);

        return 0;
}

static int janz_stop(struct net_device *ndev)
{
        struct janz_module *mod = netdev_priv(ndev);
        struct janz_device *priv = mod->parent;
        int ret;

        dev_dbg(priv->dev, "%s: called\n", __func__);

        /* stop the network device xmit routine */
        netif_stop_queue(ndev);

        /* bring the bus offline */
        ret = janz_set_bus_state(mod, false);
        if (ret) {
                dev_err(priv->dev, "unable to set bus-off\n");
                return ret;
        }

        /* stop receiving packets */
        napi_disable(&mod->napi);

        return 0;
}

static int janz_xmit(struct sk_buff *skb, struct net_device *ndev)
{
        struct janz_module *mod = netdev_priv(ndev);
        struct janz_device *priv = mod->parent;
        struct net_device_stats *stats = &ndev->stats;
        struct can_frame *cf = (struct can_frame *)skb->data;
        struct janz_fast_desc desc;
        void __iomem *desc_addr;
        unsigned long flags;

        spin_lock_irqsave(&mod->lock, flags);

        /* copy the control bits of the descriptor */
        janz_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
        desc_addr = mod->regs + ((mod->fasttx_num % 16) * sizeof(desc));
        memset(&desc, 0, sizeof(desc));
        memcpy_fromio(&desc, desc_addr, 1);

        /* check that we can actually transmit */
        if (!(desc.control & DESC_VALID)) {
                dev_err(priv->dev, "%s: no buffers\n", __func__);
                stats->tx_dropped++;
                kfree_skb(skb);
                goto out_unlock;
        }

        /* convert the CAN frame into Janz format */
        can_to_janz(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));
        janz_set_int(mod);
        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);
        dev_dbg(priv->dev, "%s: update fast TX num -> %d\n", __func__, 
mod->fasttx_num);

        /* update statistics */
        stats->tx_packets++;
        stats->tx_bytes += cf->can_dlc;
        kfree_skb(skb);

out_unlock:
        spin_unlock_irqrestore(&mod->lock, flags);
        return NETDEV_TX_OK;
}

static int janz_napi(struct napi_struct *napi, int quota)
{
        struct net_device *ndev = napi->dev;
        struct net_device_stats *stats = &ndev->stats;
        struct janz_module *mod = netdev_priv(ndev);
        struct janz_device *priv = mod->parent;
        struct janz_fast_desc desc;
        void __iomem *desc_addr;
        struct can_frame *cf;
        struct sk_buff *skb;
        unsigned long flags;
        int handled = 0;

        dev_dbg(priv->dev, "%s: modno %d called quota %d\n", __func__, 
mod->num, quota);
        spin_lock_irqsave(&mod->lock, flags);

        while (handled < quota) {

                /* copy the whole descriptor */
                janz_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
                desc_addr = mod->regs + ((mod->fastrx_num % 16) * sizeof(desc));
                memcpy_fromio(&desc, desc_addr, sizeof(desc));

                /* check that we actually have a CAN frame */
                if (!(desc.control & DESC_VALID)) {
                        dev_dbg(priv->dev, "%s: no more frames\n", __func__);
                        break;
                }

                /* allocate an skb */
                skb = alloc_can_skb(ndev, &cf);
                if (unlikely(skb == NULL)) {
                        dev_dbg(priv->dev, "%s: no more skbs\n", __func__);
                        stats->rx_dropped++;
                        goto err_noalloc;
                }

                /* convert the Janz frame into CAN format */
                janz_to_can(mod, &desc, cf);

                /* receive the skb, update statistics */
                netif_receive_skb(skb);
                stats->rx_packets++;
                stats->rx_bytes += cf->can_dlc;
                handled++;

err_noalloc:
                /* toggle the valid bit and return the descriptor to the ring */
                desc.control ^= DESC_VALID;
                memcpy_toio(desc_addr, &desc, 1);

                /* update the next buffer pointer */
                mod->fastrx_num = (desc.control & DESC_WRAP) ? 0 : 
(mod->fastrx_num + 1);
                dev_dbg(priv->dev, "%s: update fast RX num -> %d\n", __func__, 
mod->fastrx_num);
        }

        if (handled < quota)
                napi_complete(napi);

        dev_dbg(priv->dev, "%s: modno %d handled %d CAN frames\n", __func__, 
mod->num, handled);
        spin_unlock_irqrestore(&mod->lock, flags);
        return handled;
}

static const struct net_device_ops janz_netdev_ops = {
        .ndo_open       = janz_open,
        .ndo_stop       = janz_stop,
        .ndo_start_xmit = janz_xmit,
};

/*----------------------------------------------------------------------------*/
/* Low-level CAN Device                                                       */
/*----------------------------------------------------------------------------*/

struct janz_baud_entry {
        u32 rate;
        u8 btr0;
        u8 btr1;
};

static struct janz_baud_entry janz_baud_table[] = {
#if 0
        {1000000, 0x00, 0x23}, /* early sampling */
#endif
        {1000000, 0x00, 0x14}, /* late sampling */
        {500000,  0x00, 0x1c},
        {250000,  0x01, 0x1c},
        {125000,  0x03, 0x1c},
        {100000,  0xc7, 0x34},
        {50000,   0xcf, 0x34},
        {20000,   0xcf, 0x7f},
};

static int janz_set_bittiming(struct net_device *ndev)
{
        struct janz_module *mod = netdev_priv(ndev);
        struct janz_device *priv = mod->parent;
        struct janz_baud_entry *entry = NULL;
        struct janz_msg msg;
        int i, ret;

        dev_dbg(priv->dev, "%s: called bitrate %d\n",
                           __func__, mod->can.bittiming.bitrate);

        for (i = 0; i < ARRAY_SIZE(janz_baud_table); i++) {
                if (mod->can.bittiming.bitrate == janz_baud_table[i].rate) {
                        entry = &janz_baud_table[i];
                        break;
                }
        }

        if (!entry) {
                dev_dbg(priv->dev, "%s: no matching bittiming\n", __func__);
                return -EINVAL;
        }

        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] = entry->btr0;
        msg.data[3] = entry->btr1;

        ret = janz_send_msg(mod, &msg);
        if (ret) {
                dev_dbg(priv->dev, "unable to send CBTRREQ message\n");
                return ret;
        }

        return 0;
}

static int janz_set_mode(struct net_device *ndev, enum can_mode mode)
{
        struct janz_module *mod = netdev_priv(ndev);
        struct janz_device *priv = mod->parent;

        dev_dbg(priv->dev, "%s: called mode %d\n", __func__, mode);
        return 0;
}

/*----------------------------------------------------------------------------*/
/* PCI Subsystem                                                              */
/*----------------------------------------------------------------------------*/

static void janz_free_one(struct janz_device *priv, unsigned int modno)
{
        struct net_device *ndev;
        struct janz_module *mod;

        mod = priv->modules[modno];
        priv->modules[modno] = NULL;
        ndev = mod->ndev;

        unregister_netdev(ndev);
        free_netdev(ndev);

        janz_shutdown_module(mod);
}

/* setup a single CAN module on the MODULbus carrier board */
static int janz_alloc_one(struct janz_device *priv, unsigned int modno)
{
        struct net_device *ndev;
        struct janz_module *mod;
        int ret;

        ndev = alloc_candev(sizeof(*mod), 16);
        if (!ndev) {
                dev_err(priv->dev, "%s: unable to allocate CANdev\n", __func__);
                ret = -ENOMEM;
                goto out_return;
        }

        mod = netdev_priv(ndev);
        priv->modules[modno] = mod;

        mod->ndev = ndev;
        mod->parent = priv;
        mod->num = modno;
        mod->regs = priv->modulbus_regs + (0x200 * modno);
        INIT_WORK(&mod->work, janz_work);
        spin_lock_init(&mod->lock);

        /* initialize the software state */
        janz_init_module(mod);

        ndev->netdev_ops = &janz_netdev_ops;
        ndev->irq = priv->pdev->irq;
        ndev->flags |= IFF_ECHO;

        mod->can.do_set_bittiming = &janz_set_bittiming;
        mod->can.do_set_mode = &janz_set_mode;

        netif_napi_add(ndev, &mod->napi, janz_napi, 16);
        SET_NETDEV_DEV(ndev, &priv->pdev->dev);

        /* reset and initialize the CAN controller into fast mode */
        ret = janz_startup_module(mod);
        if (ret) {
                dev_err(priv->dev, "%s: unable to start CANdev\n", __func__);
                goto out_free_ndev;
        }

        /* register with the Linux CAN layer */
        ret = register_candev(ndev);
        if (ret) {
                dev_err(priv->dev, "%s: unable to register CANdev\n", __func__);
                goto out_free_ndev;
        }

        dev_info(priv->dev, "module %d: registered CAN device\n", modno);
        return 0;

out_free_ndev:
        priv->modules[modno] = NULL;
        free_netdev(ndev);
out_return:
        return ret;
}

static int janz_probe_modules(struct janz_device *priv)
{
        struct janz_module *mod;
        int i;

        for (i = 0; i < ARRAY_SIZE(janz_modules_present); i++) {
                mod = priv->modules[i];
                switch (janz_modules_present[i]) {
                case JANZ_MODULE_NONE:
                        dev_dbg(priv->dev, "modno %d: NONE\n", i);
                        break;
                case JANZ_MODULE_ICAN3:
                        dev_dbg(priv->dev, "modno %d: ICAN3\n", i);
                        janz_alloc_one(priv, i);
                        break;
                case JANZ_MODULE_TTL:
                        dev_dbg(priv->dev, "modno %d: TTL\n", i);
                        break;
                default:
                        dev_err(priv->dev, "modno %d: UNKNOWN\n", i);
                        break;
                }
        }

        return 0;
}

static void janz_remove_modules(struct janz_device *priv)
{
        struct janz_module *mod;
        int i;

        for (i = 0; i < ARRAY_SIZE(janz_modules_present); i++) {
                mod = priv->modules[i];
                if (mod == NULL)
                        continue;

                switch (janz_modules_present[i]) {
                case JANZ_MODULE_NONE:
                        dev_dbg(priv->dev, "modno %d: NONE\n", i);
                        break;
                case JANZ_MODULE_ICAN3:
                        dev_dbg(priv->dev, "modno %d: ICAN3\n", i);
                        janz_free_one(priv, i);
                        break;
                case JANZ_MODULE_TTL:
                        dev_dbg(priv->dev, "modno %d: TTL\n", i);
                        break;
                default:
                        dev_err(priv->dev, "modno %d: UNKNOWN\n", i);
                        break;
                }
        }
}

static int janz_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
        struct janz_device *priv;
        int ret;

        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv) {
                dev_err(&dev->dev, "unable to allocate private data\n");
                ret = -ENOMEM;
                goto out_return;
        }

        pci_set_drvdata(dev, priv);
        priv->dev = &dev->dev;
        priv->pdev = dev;

        /* Hardware Initialization */
        ret = pci_enable_device(dev);
        if (ret) {
                dev_err(&dev->dev, "unable to enable device\n");
                goto out_free_priv;
        }

        pci_set_master(dev);
        ret = pci_request_regions(dev, drv_name);
        if (ret) {
                dev_err(&dev->dev, "unable to request regions\n");
                goto out_pci_disable_device;
        }

        /* Local Configuration Registers */
        priv->control_regs = pci_ioremap_bar(dev, 0);
        if (!priv->control_regs) {
                dev_err(&dev->dev, "unable to remap control regs\n");
                ret = -ENOMEM;
                goto out_pci_release_regions;
        }

        /* MODULbus memory space, big endian access */
        priv->modulbus_regs = pci_ioremap_bar(dev, 3);
        if (!priv->modulbus_regs) {
                dev_err(&dev->dev, "unable to remap MODULbus regs\n");
                ret = -ENOMEM;
                goto out_unmap_control_regs;
        }

        /* Onboard configuration registers */
        priv->onboard_regs = pci_ioremap_bar(dev, 4);
        if (!priv->onboard_regs) {
                dev_err(&dev->dev, "unable to remap onboard regs\n");
                ret = -ENOMEM;
                goto out_unmap_modulbus_regs;
        }

        /* Read the hex switch on the carrier board */
        priv->hex = ioread8(priv->onboard_regs + JANZ_OB_MBUS_NUM);
        dev_info(&dev->dev, "detected board with HEX switch %X\n", priv->hex);

        /* Disable all interrupt lines, hookup the handler */
        iowrite8(0xf, priv->onboard_regs + JANZ_OB_INT_DISABLE);
        ret = request_irq(dev->irq, janz_irq, IRQF_SHARED, drv_name, priv);
        if (ret) {
                dev_err(&dev->dev, "unable to register IRQ handler\n");
                goto out_unmap_onboard_regs;
        }

        /* Probe all of the modules on the CMOD-IO carrier board */
        ret = janz_probe_modules(priv);
        if (ret) {
                dev_err(&dev->dev, "unable to probe MODULbus modules\n");
                goto out_free_irq;
        }

        return 0;

out_free_irq:
        free_irq(dev->irq, priv);
out_unmap_onboard_regs:
        iounmap(priv->onboard_regs);
out_unmap_modulbus_regs:
        iounmap(priv->modulbus_regs);
out_unmap_control_regs:
        iounmap(priv->control_regs);
out_pci_release_regions:
        pci_release_regions(dev);
out_pci_disable_device:
        pci_disable_device(dev);
out_free_priv:
        kfree(priv);
out_return:
        return ret;
}

static void janz_pci_remove(struct pci_dev *dev)
{
        struct janz_device *priv = pci_get_drvdata(dev);

        janz_remove_modules(priv);
        free_irq(dev->irq, priv);
        iounmap(priv->onboard_regs);
        iounmap(priv->modulbus_regs);
        iounmap(priv->control_regs);
        pci_release_regions(dev);
        pci_disable_device(dev);
        kfree(priv);
}

#define PCI_VENDOR_ID_JANZ              0x13c3

/* The list of devices that this module will support */
static struct pci_device_id janz_pci_ids[] = {
        { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 
},
        { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 
},
        { 0, }
};
MODULE_DEVICE_TABLE(pci, janz_pci_ids);

static struct pci_driver janz_pci_driver = {
        .name     = (char *)drv_name,
        .id_table = janz_pci_ids,
        .probe    = janz_pci_probe,
        .remove   = janz_pci_remove,
};

/*----------------------------------------------------------------------------*/
/* Module Init / Exit                                                         */
/*----------------------------------------------------------------------------*/

static int __init janz_init(void)
{
        return pci_register_driver(&janz_pci_driver);
}

static void __exit janz_exit(void)
{
        pci_unregister_driver(&janz_pci_driver);
}

MODULE_AUTHOR("Ira W. Snyder <[email protected]>");
MODULE_DESCRIPTION("Janz PCI CAN Driver");
MODULE_LICENSE("GPL");

module_init(janz_init);
module_exit(janz_exit);
_______________________________________________
Socketcan-core mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

Reply via email to