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
