Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Ira W. Snyder | 7580 | 86.38% | 8 | 29.63% |
Andreas Gröger | 1082 | 12.33% | 2 | 7.41% |
Marc Kleine-Budde | 69 | 0.79% | 2 | 7.41% |
Andri Yngvason | 9 | 0.10% | 1 | 3.70% |
Nicholas Mc Guire | 6 | 0.07% | 1 | 3.70% |
Jingoo Han | 5 | 0.06% | 2 | 7.41% |
Oliver Hartkopp | 5 | 0.06% | 1 | 3.70% |
Lucas De Marchi | 3 | 0.03% | 1 | 3.70% |
Eric Dumazet | 3 | 0.03% | 1 | 3.70% |
Alexey Dobriyan | 3 | 0.03% | 1 | 3.70% |
Samuel Ortiz | 2 | 0.02% | 1 | 3.70% |
Thomas Gleixner | 2 | 0.02% | 1 | 3.70% |
Joe Perches | 2 | 0.02% | 1 | 3.70% |
Luc Van Oostenryck | 1 | 0.01% | 1 | 3.70% |
Tejun Heo | 1 | 0.01% | 1 | 3.70% |
Axel Lin | 1 | 0.01% | 1 | 3.70% |
Arvind Yadav | 1 | 0.01% | 1 | 3.70% |
Total | 8775 | 27 |
// SPDX-License-Identifier: GPL-2.0-or-later /* * Janz MODULbus VMOD-ICAN3 CAN Interface Driver * * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu> */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/netdevice.h> #include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/skb.h> #include <linux/can/error.h> #include <linux/mfd/janz.h> #include <asm/io.h> /* the DPM has 64k of memory, organized into 256x 256 byte pages */ #define DPM_NUM_PAGES 256 #define DPM_PAGE_SIZE 256 #define DPM_PAGE_ADDR(p) ((p) * DPM_PAGE_SIZE) /* JANZ ICAN3 "old-style" host interface queue page numbers */ #define QUEUE_OLD_CONTROL 0 #define QUEUE_OLD_RB0 1 #define QUEUE_OLD_RB1 2 #define QUEUE_OLD_WB0 3 #define QUEUE_OLD_WB1 4 /* Janz ICAN3 "old-style" host interface control registers */ #define MSYNC_PEER 0x00 /* ICAN only */ #define MSYNC_LOCL 0x01 /* host only */ #define TARGET_RUNNING 0x02 #define FIRMWARE_STAMP 0x60 /* big endian firmware stamp */ #define MSYNC_RB0 0x01 #define MSYNC_RB1 0x02 #define MSYNC_RBLW 0x04 #define MSYNC_RB_MASK (MSYNC_RB0 | MSYNC_RB1) #define MSYNC_WB0 0x10 #define MSYNC_WB1 0x20 #define MSYNC_WBLW 0x40 #define MSYNC_WB_MASK (MSYNC_WB0 | MSYNC_WB1) /* Janz ICAN3 "new-style" host interface queue page numbers */ #define QUEUE_TOHOST 5 #define QUEUE_FROMHOST_MID 6 #define QUEUE_FROMHOST_HIGH 7 #define QUEUE_FROMHOST_LOW 8 /* The first free page in the DPM is #9 */ #define DPM_FREE_START 9 /* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */ #define DESC_VALID 0x80 #define DESC_WRAP 0x40 #define DESC_INTERRUPT 0x20 #define DESC_IVALID 0x10 #define DESC_LEN(len) (len) /* Janz ICAN3 Firmware Messages */ #define MSG_CONNECTI 0x02 #define MSG_DISCONNECT 0x03 #define MSG_IDVERS 0x04 #define MSG_MSGLOST 0x05 #define MSG_NEWHOSTIF 0x08 #define MSG_INQUIRY 0x0a #define MSG_SETAFILMASK 0x10 #define MSG_INITFDPMQUEUE 0x11 #define MSG_HWCONF 0x12 #define MSG_FMSGLOST 0x15 #define MSG_CEVTIND 0x37 #define MSG_CBTRREQ 0x41 #define MSG_COFFREQ 0x42 #define MSG_CONREQ 0x43 #define MSG_CCONFREQ 0x47 #define MSG_NMTS 0xb0 #define MSG_LMTS 0xb4 /* * Janz ICAN3 CAN Inquiry Message Types * * NOTE: there appears to be a firmware bug here. You must send * NOTE: INQUIRY_STATUS and expect to receive an INQUIRY_EXTENDED * NOTE: response. The controller never responds to a message with * NOTE: the INQUIRY_EXTENDED subspec :( */ #define INQUIRY_STATUS 0x00 #define INQUIRY_TERMINATION 0x01 #define INQUIRY_EXTENDED 0x04 /* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */ #define SETAFILMASK_REJECT 0x00 #define SETAFILMASK_FASTIF 0x02 /* Janz ICAN3 CAN Hardware Configuration Message Types */ #define HWCONF_TERMINATE_ON 0x01 #define HWCONF_TERMINATE_OFF 0x00 /* Janz ICAN3 CAN Event Indication Message Types */ #define CEVTIND_EI 0x01 #define CEVTIND_DOI 0x02 #define CEVTIND_LOST 0x04 #define CEVTIND_FULL 0x08 #define CEVTIND_BEI 0x10 #define CEVTIND_CHIP_SJA1000 0x02 #define ICAN3_BUSERR_QUOTA_MAX 255 /* Janz ICAN3 CAN Frame Conversion */ #define ICAN3_SNGL 0x02 #define ICAN3_ECHO 0x10 #define ICAN3_EFF_RTR 0x40 #define ICAN3_SFF_RTR 0x10 #define ICAN3_EFF 0x80 #define ICAN3_CAN_TYPE_MASK 0x0f #define ICAN3_CAN_TYPE_SFF 0x00 #define ICAN3_CAN_TYPE_EFF 0x01 #define ICAN3_CAN_DLC_MASK 0x0f /* Janz ICAN3 NMTS subtypes */ #define NMTS_CREATE_NODE_REQ 0x0 #define NMTS_SLAVE_STATE_IND 0x8 #define NMTS_SLAVE_EVENT_IND 0x9 /* Janz ICAN3 LMTS subtypes */ #define LMTS_BUSON_REQ 0x0 #define LMTS_BUSOFF_REQ 0x1 #define LMTS_CAN_CONF_REQ 0x2 /* Janz ICAN3 NMTS Event indications */ #define NE_LOCAL_OCCURRED 0x3 #define NE_LOCAL_RESOLVED 0x2 #define NE_REMOTE_OCCURRED 0xc #define NE_REMOTE_RESOLVED 0x8 /* * SJA1000 Status and Error Register Definitions * * Copied from drivers/net/can/sja1000/sja1000.h */ /* status register content */ #define SR_BS 0x80 #define SR_ES 0x40 #define SR_TS 0x20 #define SR_RS 0x10 #define SR_TCS 0x08 #define SR_TBS 0x04 #define SR_DOS 0x02 #define SR_RBS 0x01 #define SR_CRIT (SR_BS|SR_ES) /* ECC register */ #define ECC_SEG 0x1F #define ECC_DIR 0x20 #define ECC_ERR 6 #define ECC_BIT 0x00 #define ECC_FORM 0x40 #define ECC_STUFF 0x80 #define ECC_MASK 0xc0 /* Number of buffers for use in the "new-style" host interface */ #define ICAN3_NEW_BUFFERS 16 /* Number of buffers for use in the "fast" host interface */ #define ICAN3_TX_BUFFERS 512 #define ICAN3_RX_BUFFERS 1024 /* SJA1000 Clock Input */ #define ICAN3_CAN_CLOCK 8000000 /* Janz ICAN3 firmware types */ enum ican3_fwtype { ICAN3_FWTYPE_ICANOS, ICAN3_FWTYPE_CAL_CANOPEN, }; /* Driver Name */ #define DRV_NAME "janz-ican3" /* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */ struct ican3_dpm_control { /* window address register */ u8 window_address; u8 unused1; /* * Read access: clear interrupt from microcontroller * Write access: send interrupt to microcontroller */ u8 interrupt; u8 unused2; /* write-only: reset all hardware on the module */ u8 hwreset; u8 unused3; /* write-only: generate an interrupt to the TPU */ u8 tpuinterrupt; }; struct ican3_dev { /* must be the first member */ struct can_priv can; /* CAN network device */ struct net_device *ndev; struct napi_struct napi; /* module number */ unsigned int num; /* base address of registers and IRQ */ struct janz_cmodio_onboard_regs __iomem *ctrl; struct ican3_dpm_control __iomem *dpmctrl; void __iomem *dpm; int irq; /* CAN bus termination status */ struct completion termination_comp; bool termination_enabled; /* CAN bus error status registers */ struct completion buserror_comp; struct can_berr_counter bec; /* firmware type */ enum ican3_fwtype fwtype; char fwinfo[32]; /* old and new style host interface */ unsigned int iftype; /* queue for echo packets */ struct sk_buff_head echoq; /* * Any function which changes the current DPM page must hold this * lock while it is performing data accesses. This ensures that the * function will not be preempted and end up reading data from a * different DPM page than it expects. */ 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_num; unsigned int fasttx_start; unsigned int fasttx_num; /* first free DPM page */ unsigned int free_page; }; struct ican3_msg { u8 control; u8 spec; __le16 len; u8 data[252]; }; struct ican3_new_desc { u8 control; u8 pointer; }; struct ican3_fast_desc { u8 control; u8 command; u8 data[14]; }; /* write to the window basic address register */ static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page) { BUG_ON(page >= DPM_NUM_PAGES); iowrite8(page, &mod->dpmctrl->window_address); } /* * ICAN3 "old-style" host interface */ /* * Receive a message from the ICAN3 "old-style" firmware interface * * LOCKING: must hold mod->lock * * returns 0 on success, -ENOMEM when no message exists */ static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) { unsigned int mbox, mbox_page; u8 locl, peer, xord; /* get the MSYNC registers */ ican3_set_page(mod, QUEUE_OLD_CONTROL); peer = ioread8(mod->dpm + MSYNC_PEER); locl = ioread8(mod->dpm + MSYNC_LOCL); xord = locl ^ peer; if ((xord & MSYNC_RB_MASK) == 0x00) { netdev_dbg(mod->ndev, "no mbox for reading\n"); return -ENOMEM; } /* find the first free mbox to read */ if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK) mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1; else mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1; /* copy the message */ mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1; ican3_set_page(mod, mbox_page); memcpy_fromio(msg, mod->dpm, sizeof(*msg)); /* * notify the firmware that the read buffer is available * for it to fill again */ locl ^= mbox; ican3_set_page(mod, QUEUE_OLD_CONTROL); iowrite8(locl, mod->dpm + MSYNC_LOCL); return 0; } /* * Send a message through the "old-style" firmware interface * * LOCKING: must hold mod->lock * * returns 0 on success, -ENOMEM when no free space exists */ static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) { unsigned int mbox, mbox_page; u8 locl, peer, xord; /* get the MSYNC registers */ ican3_set_page(mod, QUEUE_OLD_CONTROL); peer = ioread8(mod->dpm + MSYNC_PEER); locl = ioread8(mod->dpm + MSYNC_LOCL); xord = locl ^ peer; if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) { netdev_err(mod->ndev, "no mbox for writing\n"); return -ENOMEM; } /* calculate a free mbox to use */ mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0; /* copy the message to the DPM */ mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1; ican3_set_page(mod, mbox_page); memcpy_toio(mod->dpm, msg, sizeof(*msg)); locl ^= mbox; if (mbox == MSYNC_WB1) locl |= MSYNC_WBLW; ican3_set_page(mod, QUEUE_OLD_CONTROL); iowrite8(locl, mod->dpm + MSYNC_LOCL); return 0; } /* * ICAN3 "new-style" Host Interface Setup */ static void ican3_init_new_host_interface(struct ican3_dev *mod) { struct ican3_new_desc desc; unsigned long flags; void __iomem *dst; int i; spin_lock_irqsave(&mod->lock, flags); /* setup the internal datastructures for RX */ mod->rx_num = 0; mod->rx_int = 0; /* tohost queue descriptors are in page 5 */ ican3_set_page(mod, QUEUE_TOHOST); dst = mod->dpm; /* initialize the tohost (rx) queue descriptors: pages 9-24 */ for (i = 0; i < ICAN3_NEW_BUFFERS; i++) { desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */ desc.pointer = mod->free_page; /* set wrap flag on last buffer */ if (i == ICAN3_NEW_BUFFERS - 1) desc.control |= DESC_WRAP; memcpy_toio(dst, &desc, sizeof(desc)); dst += sizeof(desc); mod->free_page++; } /* fromhost (tx) mid queue descriptors are in page 6 */ ican3_set_page(mod, QUEUE_FROMHOST_MID); dst = mod->dpm; /* setup the internal datastructures for TX */ mod->tx_num = 0; /* initialize the fromhost mid queue descriptors: pages 25-40 */ for (i = 0; i < ICAN3_NEW_BUFFERS; i++) { desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */ desc.pointer = mod->free_page; /* set wrap flag on last buffer */ if (i == ICAN3_NEW_BUFFERS - 1) desc.control |= DESC_WRAP; memcpy_toio(dst, &desc, sizeof(desc)); dst += sizeof(desc); mod->free_page++; } /* fromhost hi queue descriptors are in page 7 */ ican3_set_page(mod, QUEUE_FROMHOST_HIGH); dst = mod->dpm; /* initialize only a single buffer in the fromhost hi queue (unused) */ desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */ desc.pointer = mod->free_page; memcpy_toio(dst, &desc, sizeof(desc)); mod->free_page++; /* fromhost low queue descriptors are in page 8 */ ican3_set_page(mod, QUEUE_FROMHOST_LOW); dst = mod->dpm; /* initialize only a single buffer in the fromhost low queue (unused) */ desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */ desc.pointer = mod->free_page; memcpy_toio(dst, &desc, sizeof(desc)); mod->free_page++; spin_unlock_irqrestore(&mod->lock, flags); } /* * ICAN3 Fast Host Interface Setup */ static void ican3_init_fast_host_interface(struct ican3_dev *mod) { struct ican3_fast_desc desc; unsigned long flags; unsigned int addr; void __iomem *dst; int i; spin_lock_irqsave(&mod->lock, flags); /* save the start recv page */ mod->fastrx_start = mod->free_page; mod->fastrx_num = 0; /* build a single fast tohost queue descriptor */ memset(&desc, 0, sizeof(desc)); desc.control = 0x00; desc.command = 1; /* build the tohost queue descriptor ring in memory */ addr = 0; for (i = 0; i < ICAN3_RX_BUFFERS; i++) { /* set the wrap bit on the last buffer */ if (i == ICAN3_RX_BUFFERS - 1) desc.control |= DESC_WRAP; /* switch to the correct page */ ican3_set_page(mod, mod->free_page); /* copy the descriptor to the DPM */ dst = mod->dpm + addr; memcpy_toio(dst, &desc, sizeof(desc)); addr += sizeof(desc); /* move to the next page if necessary */ if (addr >= DPM_PAGE_SIZE) { addr = 0; mod->free_page++; } } /* make sure we page-align the next queue */ if (addr != 0) mod->free_page++; /* save the start xmit page */ mod->fasttx_start = mod->free_page; mod->fasttx_num = 0; /* build a single fast fromhost queue descriptor */ memset(&desc, 0, sizeof(desc)); desc.control = DESC_VALID; desc.command = 1; /* build the fromhost queue descriptor ring in memory */ addr = 0; for (i = 0; i < ICAN3_TX_BUFFERS; i++) { /* set the wrap bit on the last buffer */ if (i == ICAN3_TX_BUFFERS - 1) desc.control |= DESC_WRAP; /* switch to the correct page */ ican3_set_page(mod, mod->free_page); /* copy the descriptor to the DPM */ dst = mod->dpm + addr; memcpy_toio(dst, &desc, sizeof(desc)); addr += sizeof(desc); /* move to the next page if necessary */ if (addr >= DPM_PAGE_SIZE) { addr = 0; mod->free_page++; } } spin_unlock_irqrestore(&mod->lock, flags); } /* * ICAN3 "new-style" Host Interface Message Helpers */ /* * LOCKING: must hold mod->lock */ static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) { struct ican3_new_desc desc; void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc)); /* switch to the fromhost mid queue, and read the buffer descriptor */ ican3_set_page(mod, QUEUE_FROMHOST_MID); memcpy_fromio(&desc, desc_addr, sizeof(desc)); if (!(desc.control & DESC_VALID)) { netdev_dbg(mod->ndev, "%s: no free buffers\n", __func__); return -ENOMEM; } /* switch to the data page, copy the data */ ican3_set_page(mod, desc.pointer); memcpy_toio(mod->dpm, msg, sizeof(*msg)); /* switch back to the descriptor, set the valid bit, write it back */ ican3_set_page(mod, QUEUE_FROMHOST_MID); desc.control ^= DESC_VALID; memcpy_toio(desc_addr, &desc, sizeof(desc)); /* update the tx number */ mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1); return 0; } /* * LOCKING: must hold mod->lock */ static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) { struct ican3_new_desc desc; void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc)); /* switch to the tohost queue, and read the buffer descriptor */ ican3_set_page(mod, QUEUE_TOHOST); memcpy_fromio(&desc, desc_addr, sizeof(desc)); if (!(desc.control & DESC_VALID)) { netdev_dbg(mod->ndev, "%s: no buffers to recv\n", __func__); return -ENOMEM; } /* switch to the data page, copy the data */ ican3_set_page(mod, desc.pointer); memcpy_fromio(msg, mod->dpm, sizeof(*msg)); /* switch back to the descriptor, toggle the valid bit, write it back */ ican3_set_page(mod, QUEUE_TOHOST); desc.control ^= DESC_VALID; memcpy_toio(desc_addr, &desc, sizeof(desc)); /* update the rx number */ mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1); return 0; } /* * Message Send / Recv Helpers */ static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) { unsigned long flags; int ret; spin_lock_irqsave(&mod->lock, flags); if (mod->iftype == 0) ret = ican3_old_send_msg(mod, msg); else ret = ican3_new_send_msg(mod, msg); spin_unlock_irqrestore(&mod->lock, flags); return ret; } static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) { unsigned long flags; int ret; spin_lock_irqsave(&mod->lock, flags); if (mod->iftype == 0) ret = ican3_old_recv_msg(mod, msg); else ret = ican3_new_recv_msg(mod, msg); spin_unlock_irqrestore(&mod->lock, flags); return ret; } /* * Quick Pre-constructed Messages */ static int ican3_msg_connect(struct ican3_dev *mod) { struct ican3_msg msg; memset(&msg, 0, sizeof(msg)); msg.spec = MSG_CONNECTI; msg.len = cpu_to_le16(0); return ican3_send_msg(mod, &msg); } static int ican3_msg_disconnect(struct ican3_dev *mod) { struct ican3_msg msg; memset(&msg, 0, sizeof(msg)); msg.spec = MSG_DISCONNECT; msg.len = cpu_to_le16(0); return ican3_send_msg(mod, &msg); } static int ican3_msg_newhostif(struct ican3_dev *mod) { struct ican3_msg msg; int ret; memset(&msg, 0, sizeof(msg)); msg.spec = MSG_NEWHOSTIF; msg.len = cpu_to_le16(0); /* If we're not using the old interface, switching seems bogus */ WARN_ON(mod->iftype != 0); ret = ican3_send_msg(mod, &msg); if (ret) return ret; /* mark the module as using the new host interface */ mod->iftype = 1; return 0; } static int ican3_msg_fasthostif(struct ican3_dev *mod) { struct ican3_msg msg; unsigned int addr; memset(&msg, 0, sizeof(msg)); msg.spec = MSG_INITFDPMQUEUE; msg.len = cpu_to_le16(8); /* write the tohost queue start address */ addr = DPM_PAGE_ADDR(mod->fastrx_start); msg.data[0] = addr & 0xff; msg.data[1] = (addr >> 8) & 0xff; msg.data[2] = (addr >> 16) & 0xff; msg.data[3] = (addr >> 24) & 0xff; /* write the fromhost queue start address */ addr = DPM_PAGE_ADDR(mod->fasttx_start); msg.data[4] = addr & 0xff; msg.data[5] = (addr >> 8) & 0xff; msg.data[6] = (addr >> 16) & 0xff; msg.data[7] = (addr >> 24) & 0xff; /* If we're not using the new interface yet, we cannot do this */ WARN_ON(mod->iftype != 1); return ican3_send_msg(mod, &msg); } /* * Setup the CAN filter to either accept or reject all * messages from the CAN bus. */ static int ican3_set_id_filter(struct ican3_dev *mod, bool accept) { struct ican3_msg msg; int ret; /* Standard Frame Format */ memset(&msg, 0, sizeof(msg)); msg.spec = MSG_SETAFILMASK; msg.len = cpu_to_le16(5); msg.data[0] = 0x00; /* IDLo LSB */ msg.data[1] = 0x00; /* IDLo MSB */ msg.data[2] = 0xff; /* IDHi LSB */ msg.data[3] = 0x07; /* IDHi MSB */ /* accept all frames for fast host if, or reject all frames */ msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT; ret = ican3_send_msg(mod, &msg); if (ret) return ret; /* Extended Frame Format */ memset(&msg, 0, sizeof(msg)); msg.spec = MSG_SETAFILMASK; msg.len = cpu_to_le16(13); msg.data[0] = 0; /* MUX = 0 */ msg.data[1] = 0x00; /* IDLo LSB */ msg.data[2] = 0x00; msg.data[3] = 0x00; msg.data[4] = 0x20; /* IDLo MSB */ msg.data[5] = 0xff; /* IDHi LSB */ msg.data[6] = 0xff; msg.data[7] = 0xff; msg.data[8] = 0x3f; /* IDHi MSB */ /* accept all frames for fast host if, or reject all frames */ msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT; return ican3_send_msg(mod, &msg); } /* * Bring the CAN bus online or offline */ static int ican3_set_bus_state(struct ican3_dev *mod, bool on) { struct can_bittiming *bt = &mod->can.bittiming; struct ican3_msg msg; u8 btr0, btr1; int res; /* This algorithm was stolen from drivers/net/can/sja1000/sja1000.c */ /* The bittiming register command for the ICAN3 just sets the bit timing */ /* registers on the SJA1000 chip directly */ btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | (((bt->phase_seg2 - 1) & 0x7) << 4); if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) btr1 |= 0x80; if (mod->fwtype == ICAN3_FWTYPE_ICANOS) { if (on) { /* set bittiming */ memset(&msg, 0, sizeof(msg)); msg.spec = MSG_CBTRREQ; msg.len = cpu_to_le16(4); msg.data[0] = 0x00; msg.data[1] = 0x00; msg.data[2] = btr0; msg.data[3] = btr1; res = ican3_send_msg(mod, &msg); if (res) return res; } /* can-on/off request */ memset(&msg, 0, sizeof(msg)); msg.spec = on ? MSG_CONREQ : MSG_COFFREQ; msg.len = cpu_to_le16(0); return ican3_send_msg(mod, &msg); } else if (mod->fwtype == ICAN3_FWTYPE_CAL_CANOPEN) { /* bittiming + can-on/off request */ memset(&msg, 0, sizeof(msg)); msg.spec = MSG_LMTS; if (on) { msg.len = cpu_to_le16(4); msg.data[0] = LMTS_BUSON_REQ; msg.data[1] = 0; msg.data[2] = btr0; msg.data[3] = btr1; } else { msg.len = cpu_to_le16(2); msg.data[0] = LMTS_BUSOFF_REQ; msg.data[1] = 0; } res = ican3_send_msg(mod, &msg); if (res) return res; if (on) { /* create NMT Slave Node for error processing * class 2 (with error capability, see CiA/DS203-1) * id 1 * name locnod1 (must be exactly 7 bytes) */ memset(&msg, 0, sizeof(msg)); msg.spec = MSG_NMTS; msg.len = cpu_to_le16(11); msg.data[0] = NMTS_CREATE_NODE_REQ; msg.data[1] = 0; msg.data[2] = 2; /* node class */ msg.data[3] = 1; /* node id */ strcpy(msg.data + 4, "locnod1"); /* node name */ return ican3_send_msg(mod, &msg); } return 0; } return -ENOTSUPP; } static int ican3_set_termination(struct ican3_dev *mod, bool on) { struct ican3_msg msg; memset(&msg, 0, sizeof(msg)); msg.spec = MSG_HWCONF; msg.len = cpu_to_le16(2); msg.data[0] = 0x00; msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF; return ican3_send_msg(mod, &msg); } static int ican3_send_inquiry(struct ican3_dev *mod, u8 subspec) { struct ican3_msg msg; memset(&msg, 0, sizeof(msg)); msg.spec = MSG_INQUIRY; msg.len = cpu_to_le16(2); msg.data[0] = subspec; msg.data[1] = 0x00; return ican3_send_msg(mod, &msg); } static int ican3_set_buserror(struct ican3_dev *mod, u8 quota) { struct ican3_msg msg; if (mod->fwtype == ICAN3_FWTYPE_ICANOS) { memset(&msg, 0, sizeof(msg)); msg.spec = MSG_CCONFREQ; msg.len = cpu_to_le16(2); msg.data[0] = 0x00; msg.data[1] = quota; } else if (mod->fwtype == ICAN3_FWTYPE_CAL_CANOPEN) { memset(&msg, 0, sizeof(msg)); msg.spec = MSG_LMTS; msg.len = cpu_to_le16(4); msg.data[0] = LMTS_CAN_CONF_REQ; msg.data[1] = 0x00; msg.data[2] = 0x00; msg.data[3] = quota; } else { return -ENOTSUPP; } return ican3_send_msg(mod, &msg); } /* * ICAN3 to Linux CAN Frame Conversion */ static void ican3_to_can_frame(struct ican3_dev *mod, struct ican3_fast_desc *desc, struct can_frame *cf) { if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) { if (desc->data[1] & ICAN3_SFF_RTR) cf->can_id |= CAN_RTR_FLAG; cf->can_id |= desc->data[0] << 3; cf->can_id |= (desc->data[1] & 0xe0) >> 5; cf->can_dlc = get_can_dlc(desc->data[1] & ICAN3_CAN_DLC_MASK); memcpy(cf->data, &desc->data[2], cf->can_dlc); } else { cf->can_dlc = get_can_dlc(desc->data[0] & ICAN3_CAN_DLC_MASK); if (desc->data[0] & ICAN3_EFF_RTR) cf->can_id |= CAN_RTR_FLAG; if (desc->data[0] & ICAN3_EFF) { cf->can_id |= CAN_EFF_FLAG; cf->can_id |= desc->data[2] << 21; /* 28-21 */ cf->can_id |= desc->data[3] << 13; /* 20-13 */ cf->can_id |= desc->data[4] << 5; /* 12-5 */ cf->can_id |= (desc->data[5] & 0xf8) >> 3; } else { cf->can_id |= desc->data[2] << 3; /* 10-3 */ cf->can_id |= desc->data[3] >> 5; /* 2-0 */ } memcpy(cf->data, &desc->data[6], cf->can_dlc); } } static void can_frame_to_ican3(struct ican3_dev *mod, struct can_frame *cf, struct ican3_fast_desc *desc) { /* clear out any stale data in the descriptor */ memset(desc->data, 0, sizeof(desc->data)); /* we always use the extended format, with the ECHO flag set */ desc->command = ICAN3_CAN_TYPE_EFF; desc->data[0] |= cf->can_dlc; desc->data[1] |= ICAN3_ECHO; /* support single transmission (no retries) mode */ if (mod->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) desc->data[1] |= ICAN3_SNGL; if (cf->can_id & CAN_RTR_FLAG) desc->data[0] |= ICAN3_EFF_RTR; /* pack the id into the correct places */ if (cf->can_id & CAN_EFF_FLAG) { desc->data[0] |= ICAN3_EFF; desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */ desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */ desc->data[4] = (cf->can_id & 0x00001fe0) >> 5; /* 12-5 */ desc->data[5] = (cf->can_id & 0x0000001f) << 3; /* 4-0 */ } else { 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, cf->can_dlc); } /* * Interrupt Handling */ /* * Handle an ID + Version message response from the firmware. We never generate * this message in production code, but it is very useful when debugging to be * able to display this message. */ static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg) { netdev_dbg(mod->ndev, "IDVERS response: %s\n", msg->data); } static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg) { struct net_device *dev = mod->ndev; struct net_device_stats *stats = &dev->stats; struct can_frame *cf; struct sk_buff *skb; /* * Report that communication messages with the microcontroller firmware * are being lost. These are never CAN frames, so we do not generate an * error frame for userspace */ if (msg->spec == MSG_MSGLOST) { netdev_err(mod->ndev, "lost %d control messages\n", msg->data[0]); return; } /* * Oops, this indicates that we have lost messages in the fast queue, * which are exclusively CAN messages. Our driver isn't reading CAN * frames fast enough. * * We'll pretend that the SJA1000 told us that it ran out of buffer * space, because there is not a better message for this. */ skb = alloc_can_err_skb(dev, &cf); if (skb) { cf->can_id |= CAN_ERR_CRTL; cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; stats->rx_over_errors++; stats->rx_errors++; netif_rx(skb); } } /* * Handle CAN Event Indication Messages from the firmware * * The ICAN3 firmware provides the values of some SJA1000 registers when it * generates this message. The code below is largely copied from the * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary */ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) { struct net_device *dev = mod->ndev; struct net_device_stats *stats = &dev->stats; enum can_state state = mod->can.state; u8 isrc, ecc, status, rxerr, txerr; struct can_frame *cf; struct sk_buff *skb; /* we can only handle the SJA1000 part */ if (msg->data[1] != CEVTIND_CHIP_SJA1000) { netdev_err(mod->ndev, "unable to handle errors on non-SJA1000\n"); return -ENODEV; } /* check the message length for sanity */ if (le16_to_cpu(msg->len) < 6) { netdev_err(mod->ndev, "error message too short\n"); return -EINVAL; } isrc = msg->data[0]; ecc = msg->data[2]; status = msg->data[3]; rxerr = msg->data[4]; txerr = msg->data[5]; /* * This hardware lacks any support other than bus error messages to * determine if packet transmission has failed. * * When TX errors happen, one echo skb needs to be dropped from the * front of the queue. * * A small bit of code is duplicated here and below, to avoid error * skb allocation when it will just be freed immediately. */ if (isrc == CEVTIND_BEI) { int ret; netdev_dbg(mod->ndev, "bus error interrupt\n"); /* TX error */ if (!(ecc & ECC_DIR)) { kfree_skb(skb_dequeue(&mod->echoq)); stats->tx_errors++; } else { stats->rx_errors++; } /* * The controller automatically disables bus-error interrupts * and therefore we must re-enable them. */ ret = ican3_set_buserror(mod, 1); if (ret) { netdev_err(mod->ndev, "unable to re-enable bus-error\n"); return ret; } /* bus error reporting is off, return immediately */ if (!(mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) return 0; } skb = alloc_can_err_skb(dev, &cf); if (skb == NULL) return -ENOMEM; /* data overrun interrupt */ if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) { netdev_dbg(mod->ndev, "data overrun interrupt\n"); cf->can_id |= CAN_ERR_CRTL; cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; stats->rx_over_errors++; stats->rx_errors++; } /* error warning + passive interrupt */ if (isrc == CEVTIND_EI) { netdev_dbg(mod->ndev, "error warning + passive interrupt\n"); if (status & SR_BS) { state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; mod->can.can_stats.bus_off++; can_bus_off(dev); } else if (status & SR_ES) { if (rxerr >= 128 || txerr >= 128) state = CAN_STATE_ERROR_PASSIVE; else state = CAN_STATE_ERROR_WARNING; } else { state = CAN_STATE_ERROR_ACTIVE; } } /* bus error interrupt */ if (isrc == CEVTIND_BEI) { mod->can.can_stats.bus_error++; cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; switch (ecc & ECC_MASK) { case ECC_BIT: cf->data[2] |= CAN_ERR_PROT_BIT; break; case ECC_FORM: cf->data[2] |= CAN_ERR_PROT_FORM; break; case ECC_STUFF: cf->data[2] |= CAN_ERR_PROT_STUFF; break; default: cf->data[3] = ecc & ECC_SEG; break; } if (!(ecc & ECC_DIR)) cf->data[2] |= CAN_ERR_PROT_TX; cf->data[6] = txerr; cf->data[7] = rxerr; } if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING || state == CAN_STATE_ERROR_PASSIVE)) { cf->can_id |= CAN_ERR_CRTL; if (state == CAN_STATE_ERROR_WARNING) { mod->can.can_stats.error_warning++; cf->data[1] = (txerr > rxerr) ? CAN_ERR_CRTL_TX_WARNING : CAN_ERR_CRTL_RX_WARNING; } else { mod->can.can_stats.error_passive++; cf->data[1] = (txerr > rxerr) ? CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE; } cf->data[6] = txerr; cf->data[7] = rxerr; } mod->can.state = state; netif_rx(skb); return 0; } static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg) { switch (msg->data[0]) { case INQUIRY_STATUS: case INQUIRY_EXTENDED: mod->bec.rxerr = msg->data[5]; mod->bec.txerr = msg->data[6]; complete(&mod->buserror_comp); break; case INQUIRY_TERMINATION: mod->termination_enabled = msg->data[6] & HWCONF_TERMINATE_ON; complete(&mod->termination_comp); break; default: netdev_err(mod->ndev, "received an unknown inquiry response\n"); break; } } /* Handle NMTS Slave Event Indication Messages from the firmware */ static void ican3_handle_nmtsind(struct ican3_dev *mod, struct ican3_msg *msg) { u16 subspec; subspec = msg->data[0] + msg->data[1] * 0x100; if (subspec == NMTS_SLAVE_EVENT_IND) { switch (msg->data[2]) { case NE_LOCAL_OCCURRED: case NE_LOCAL_RESOLVED: /* now follows the same message as Raw ICANOS CEVTIND * shift the data at the same place and call this method */ le16_add_cpu(&msg->len, -3); memmove(msg->data, msg->data + 3, le16_to_cpu(msg->len)); ican3_handle_cevtind(mod, msg); break; case NE_REMOTE_OCCURRED: case NE_REMOTE_RESOLVED: /* should not occurre, ignore */ break; default: netdev_warn(mod->ndev, "unknown NMTS event indication %x\n", msg->data[2]); break; } } else if (subspec == NMTS_SLAVE_STATE_IND) { /* ignore state indications */ } else { netdev_warn(mod->ndev, "unhandled NMTS indication %x\n", subspec); return; } } static void ican3_handle_unknown_message(struct ican3_dev *mod, struct ican3_msg *msg) { netdev_warn(mod->ndev, "received unknown message: spec 0x%.2x length %d\n", msg->spec, le16_to_cpu(msg->len)); } /* * Handle a control message from the firmware */ static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg) { netdev_dbg(mod->ndev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__, mod->num, msg->spec, le16_to_cpu(msg->len)); switch (msg->spec) { case MSG_IDVERS: ican3_handle_idvers(mod, msg); break; case MSG_MSGLOST: case MSG_FMSGLOST: ican3_handle_msglost(mod, msg); break; case MSG_CEVTIND: ican3_handle_cevtind(mod, msg); break; case MSG_INQUIRY: ican3_handle_inquiry(mod, msg); break; case MSG_NMTS: ican3_handle_nmtsind(mod, msg); break; default: ican3_handle_unknown_message(mod, msg); break; } } /* * The ican3 needs to store all echo skbs, and therefore cannot * use the generic infrastructure for this. */ static void ican3_put_echo_skb(struct ican3_dev *mod, struct sk_buff *skb) { skb = can_create_echo_skb(skb); if (!skb) return; /* save this skb for tx interrupt echo handling */ skb_queue_tail(&mod->echoq, skb); } static unsigned int ican3_get_echo_skb(struct ican3_dev *mod) { struct sk_buff *skb = skb_dequeue(&mod->echoq); struct can_frame *cf; u8 dlc; /* this should never trigger unless there is a driver bug */ if (!skb) { netdev_err(mod->ndev, "BUG: echo skb not occupied\n"); return 0; } cf = (struct can_frame *)skb->data; dlc = cf->can_dlc; /* check flag whether this packet has to be looped back */ if (skb->pkt_type != PACKET_LOOPBACK) { kfree_skb(skb); return dlc; } skb->protocol = htons(ETH_P_CAN); skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; skb->dev = mod->ndev; netif_receive_skb(skb); return dlc; } /* * Compare an skb with an existing echo skb * * This function will be used on devices which have a hardware loopback. * On these devices, this function can be used to compare a received skb * with the saved echo skbs so that the hardware echo skb can be dropped. * * Returns true if the skb's are identical, false otherwise. */ static bool ican3_echo_skb_matches(struct ican3_dev *mod, struct sk_buff *skb) { struct can_frame *cf = (struct can_frame *)skb->data; struct sk_buff *echo_skb = skb_peek(&mod->echoq); struct can_frame *echo_cf; if (!echo_skb) return false; echo_cf = (struct can_frame *)echo_skb->data; if (cf->can_id != echo_cf->can_id) return false; if (cf->can_dlc != echo_cf->can_dlc) return false; return memcmp(cf->data, echo_cf->data, cf->can_dlc) == 0; } /* * Check that there is room in the TX ring to transmit another skb * * LOCKING: must hold mod->lock */ static bool ican3_txok(struct ican3_dev *mod) { struct ican3_fast_desc __iomem *desc; u8 control; /* check that we have echo queue space */ if (skb_queue_len(&mod->echoq) >= ICAN3_TX_BUFFERS) return false; /* copy the control bits of the descriptor */ ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16)); desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc)); control = ioread8(&desc->control); /* if the control bits are not valid, then we have no more space */ if (!(control & DESC_VALID)) return false; return true; } /* * Receive one CAN frame from the hardware * * CONTEXT: must be called from user context */ static int ican3_recv_skb(struct ican3_dev *mod) { struct net_device *ndev = mod->ndev; struct net_device_stats *stats = &ndev->stats; struct ican3_fast_desc desc; void __iomem *desc_addr; struct can_frame *cf; struct sk_buff *skb; unsigned long flags; spin_lock_irqsave(&mod->lock, flags); /* copy the whole descriptor */ ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16)); desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc)); memcpy_fromio(&desc, desc_addr, sizeof(desc)); spin_unlock_irqrestore(&mod->lock, flags); /* check that we actually have a CAN frame */ if (!(desc.control & DESC_VALID)) return -ENOBUFS; /* allocate an skb */ skb = alloc_can_skb(ndev, &cf); if (unlikely(skb == NULL)) { stats->rx_dropped++; goto err_noalloc; } /* convert the ICAN3 frame into Linux CAN format */ ican3_to_can_frame(mod, &desc, cf); /* * If this is an ECHO frame received from the hardware loopback * feature, use the skb saved in the ECHO stack instead. This allows * the Linux CAN core to support CAN_RAW_RECV_OWN_MSGS correctly. * * Since this is a confirmation of a successfully transmitted packet * sent from this host, update the transmit statistics. * * Also, the netdevice queue needs to be allowed to send packets again. */ if (ican3_echo_skb_matches(mod, skb)) { stats->tx_packets++; stats->tx_bytes += ican3_get_echo_skb(mod); kfree_skb(skb); goto err_noalloc; } /* update statistics, receive the skb */ stats->rx_packets++; stats->rx_bytes += cf->can_dlc; netif_receive_skb(skb); err_noalloc: /* toggle the valid bit and return the descriptor to the ring */ desc.control ^= DESC_VALID; spin_lock_irqsave(&mod->lock, flags); ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16)); memcpy_toio(desc_addr, &desc, 1); /* update the next buffer pointer */ mod->fastrx_num = (desc.control & DESC_WRAP) ? 0 : (mod->fastrx_num + 1); /* there are still more buffers to process */ spin_unlock_irqrestore(&mod->lock, flags); return 0; } static int ican3_napi(struct napi_struct *napi, int budget) { struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi); unsigned long flags; int received = 0; int ret; /* process all communication messages */ while (true) { struct ican3_msg uninitialized_var(msg); ret = ican3_recv_msg(mod, &msg); if (ret) break; ican3_handle_message(mod, &msg); } /* process all CAN frames from the fast interface */ while (received < budget) { ret = ican3_recv_skb(mod); if (ret) break; received++; } /* We have processed all packets that the adapter had, but it * was less than our budget, stop polling */ if (received < budget) napi_complete_done(napi, received); spin_lock_irqsave(&mod->lock, flags); /* Wake up the transmit queue if necessary */ if (netif_queue_stopped(mod->ndev) && ican3_txok(mod)) netif_wake_queue(mod->ndev); spin_unlock_irqrestore(&mod->lock, flags); /* re-enable interrupt generation */ iowrite8(1 << mod->num, &mod->ctrl->int_enable); return received; } static irqreturn_t ican3_irq(int irq, void *dev_id) { struct ican3_dev *mod = dev_id; u8 stat; /* * The interrupt status register on this device reports interrupts * as zeroes instead of using ones like most other devices */ stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num); if (stat == (1 << mod->num)) return IRQ_NONE; /* clear the MODULbus interrupt from the microcontroller */ ioread8(&mod->dpmctrl->interrupt); /* disable interrupt generation, schedule the NAPI poller */ iowrite8(1 << mod->num, &mod->ctrl->int_disable); napi_schedule(&mod->napi); return IRQ_HANDLED; } /* * Firmware reset, startup, and shutdown */ /* * Reset an ICAN module to its power-on state * * CONTEXT: no network device registered */ static int ican3_reset_module(struct ican3_dev *mod) { unsigned long start; u8 runold, runnew; /* disable interrupts so no more work is scheduled */ iowrite8(1 << mod->num, &mod->ctrl->int_disable); /* the first unallocated page in the DPM is #9 */ mod->free_page = DPM_FREE_START; ican3_set_page(mod, QUEUE_OLD_CONTROL); runold = ioread8(mod->dpm + TARGET_RUNNING); /* reset the module */ iowrite8(0x00, &mod->dpmctrl->hwreset); /* wait until the module has finished resetting and is running */ start = jiffies; do { ican3_set_page(mod, QUEUE_OLD_CONTROL); runnew = ioread8(mod->dpm + TARGET_RUNNING); if (runnew == (runold ^ 0xff)) return 0; msleep(10); } while (time_before(jiffies, start + HZ / 2)); netdev_err(mod->ndev, "failed to reset CAN module\n"); return -ETIMEDOUT; } static void ican3_shutdown_module(struct ican3_dev *mod) { ican3_msg_disconnect(mod); ican3_reset_module(mod); } /* * Startup an ICAN module, bringing it into fast mode */ static int ican3_startup_module(struct ican3_dev *mod) { int ret; ret = ican3_reset_module(mod); if (ret) { netdev_err(mod->ndev, "unable to reset module\n"); return ret; } /* detect firmware */ memcpy_fromio(mod->fwinfo, mod->dpm + FIRMWARE_STAMP, sizeof(mod->fwinfo) - 1); if (strncmp(mod->fwinfo, "JANZ-ICAN3", 10)) { netdev_err(mod->ndev, "ICAN3 not detected (found %s)\n", mod->fwinfo); return -ENODEV; } if (strstr(mod->fwinfo, "CAL/CANopen")) mod->fwtype = ICAN3_FWTYPE_CAL_CANOPEN; else mod->fwtype = ICAN3_FWTYPE_ICANOS; /* re-enable interrupts so we can send messages */ iowrite8(1 << mod->num, &mod->ctrl->int_enable); ret = ican3_msg_connect(mod); if (ret) { netdev_err(mod->ndev, "unable to connect to module\n"); return ret; } ican3_init_new_host_interface(mod); ret = ican3_msg_newhostif(mod); if (ret) { netdev_err(mod->ndev, "unable to switch to new-style interface\n"); return ret; } /* default to "termination on" */ ret = ican3_set_termination(mod, true); if (ret) { netdev_err(mod->ndev, "unable to enable termination\n"); return ret; } /* default to "bus errors enabled" */ ret = ican3_set_buserror(mod, 1); if (ret) { netdev_err(mod->ndev, "unable to set bus-error\n"); return ret; } ican3_init_fast_host_interface(mod); ret = ican3_msg_fasthostif(mod); if (ret) { netdev_err(mod->ndev, "unable to switch to fast host interface\n"); return ret; } ret = ican3_set_id_filter(mod, true); if (ret) { netdev_err(mod->ndev, "unable to set acceptance filter\n"); return ret; } return 0; } /* * CAN Network Device */ static int ican3_open(struct net_device *ndev) { struct ican3_dev *mod = netdev_priv(ndev); int ret; /* open the CAN layer */ ret = open_candev(ndev); if (ret) { netdev_err(mod->ndev, "unable to start CAN layer\n"); return ret; } /* bring the bus online */ ret = ican3_set_bus_state(mod, true); if (ret) { netdev_err(mod->ndev, "unable to set bus-on\n"); close_candev(ndev); return ret; } /* start up the network device */ mod->can.state = CAN_STATE_ERROR_ACTIVE; netif_start_queue(ndev); return 0; } static int ican3_stop(struct net_device *ndev) { struct ican3_dev *mod = netdev_priv(ndev); int ret; /* stop the network device xmit routine */ netif_stop_queue(ndev); mod->can.state = CAN_STATE_STOPPED; /* bring the bus offline, stop receiving packets */ ret = ican3_set_bus_state(mod, false); if (ret) { netdev_err(mod->ndev, "unable to set bus-off\n"); return ret; } /* drop all outstanding echo skbs */ skb_queue_purge(&mod->echoq); /* close the CAN layer */ close_candev(ndev); return 0; } static netdev_tx_t ican3_xmit(struct sk_buff *skb, struct net_device *ndev) { struct ican3_dev *mod = netdev_priv(ndev); struct can_frame *cf = (struct can_frame *)skb->data; struct ican3_fast_desc desc; void __iomem *desc_addr; unsigned long flags; if (can_dropped_invalid_skb(ndev, skb)) return NETDEV_TX_OK; spin_lock_irqsave(&mod->lock, flags); /* check that we can actually transmit */ if (!ican3_txok(mod)) { netdev_err(mod->ndev, "BUG: no free descriptors\n"); spin_unlock_irqrestore(&mod->lock, flags); return NETDEV_TX_BUSY; } /* copy the control bits of the descriptor */ ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16)); desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc)); memset(&desc, 0, sizeof(desc)); memcpy_fromio(&desc, desc_addr, 1); /* convert the Linux CAN frame into ICAN3 format */ can_frame_to_ican3(mod, cf, &desc); /* * This hardware doesn't have TX-done notifications, so we'll try and * emulate it the best we can using ECHO skbs. Add the skb to the ECHO * stack. Upon packet reception, check if the ECHO skb and received * skb match, and use that to wake the queue. */ ican3_put_echo_skb(mod, skb); /* * the programming manual says that you must set the IVALID bit, then * interrupt, then set the valid bit. Quite weird, but it seems to be * required for this to work */ desc.control |= DESC_IVALID; memcpy_toio(desc_addr, &desc, sizeof(desc)); /* generate a MODULbus interrupt to the microcontroller */ iowrite8(0x01, &mod->dpmctrl->interrupt); desc.control ^= DESC_VALID; memcpy_toio(desc_addr, &desc, sizeof(desc)); /* update the next buffer pointer */ mod->fasttx_num = (desc.control & DESC_WRAP) ? 0 : (mod->fasttx_num + 1); /* if there is no free descriptor space, stop the transmit queue */ if (!ican3_txok(mod)) netif_stop_queue(ndev); spin_unlock_irqrestore(&mod->lock, flags); return NETDEV_TX_OK; } static const struct net_device_ops ican3_netdev_ops = { .ndo_open = ican3_open, .ndo_stop = ican3_stop, .ndo_start_xmit = ican3_xmit, .ndo_change_mtu = can_change_mtu, }; /* * Low-level CAN Device */ /* This structure was stolen from drivers/net/can/sja1000/sja1000.c */ static const struct can_bittiming_const ican3_bittiming_const = { .name = DRV_NAME, .tseg1_min = 1, .tseg1_max = 16, .tseg2_min = 1, .tseg2_max = 8, .sjw_max = 4, .brp_min = 1, .brp_max = 64, .brp_inc = 1, }; static int ican3_set_mode(struct net_device *ndev, enum can_mode mode) { struct ican3_dev *mod = netdev_priv(ndev); int ret; if (mode != CAN_MODE_START) return -ENOTSUPP; /* bring the bus online */ ret = ican3_set_bus_state(mod, true); if (ret) { netdev_err(ndev, "unable to set bus-on\n"); return ret; } /* start up the network device */ mod->can.state = CAN_STATE_ERROR_ACTIVE; if (netif_queue_stopped(ndev)) netif_wake_queue(ndev); return 0; } static int ican3_get_berr_counter(const struct net_device *ndev, struct can_berr_counter *bec) { struct ican3_dev *mod = netdev_priv(ndev); int ret; ret = ican3_send_inquiry(mod, INQUIRY_STATUS); if (ret) return ret; if (!wait_for_completion_timeout(&mod->buserror_comp, HZ)) { netdev_info(mod->ndev, "%s timed out\n", __func__); return -ETIMEDOUT; } bec->rxerr = mod->bec.rxerr; bec->txerr = mod->bec.txerr; return 0; } /* * Sysfs Attributes */ static ssize_t ican3_sysfs_show_term(struct device *dev, struct device_attribute *attr, char *buf) { struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); int ret; ret = ican3_send_inquiry(mod, INQUIRY_TERMINATION); if (ret) return ret; if (!wait_for_completion_timeout(&mod->termination_comp, HZ)) { netdev_info(mod->ndev, "%s timed out\n", __func__); return -ETIMEDOUT; } return snprintf(buf, PAGE_SIZE, "%u\n", mod->termination_enabled); } static ssize_t ican3_sysfs_set_term(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); unsigned long enable; int ret; if (kstrtoul(buf, 0, &enable)) return -EINVAL; ret = ican3_set_termination(mod, enable); if (ret) return ret; return count; } static ssize_t ican3_sysfs_show_fwinfo(struct device *dev, struct device_attribute *attr, char *buf) { struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); return scnprintf(buf, PAGE_SIZE, "%s\n", mod->fwinfo); } static DEVICE_ATTR(termination, 0644, ican3_sysfs_show_term, ican3_sysfs_set_term); static DEVICE_ATTR(fwinfo, 0444, ican3_sysfs_show_fwinfo, NULL); static struct attribute *ican3_sysfs_attrs[] = { &dev_attr_termination.attr, &dev_attr_fwinfo.attr, NULL, }; static const struct attribute_group ican3_sysfs_attr_group = { .attrs = ican3_sysfs_attrs, }; /* * PCI Subsystem */ static int ican3_probe(struct platform_device *pdev) { struct janz_platform_data *pdata; struct net_device *ndev; struct ican3_dev *mod; struct resource *res; struct device *dev; int ret; pdata = dev_get_platdata(&pdev->dev); if (!pdata) return -ENXIO; dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno); /* save the struct device for printing */ dev = &pdev->dev; /* allocate the CAN device and private data */ ndev = alloc_candev(sizeof(*mod), 0); if (!ndev) { dev_err(dev, "unable to allocate CANdev\n"); ret = -ENOMEM; goto out_return; } platform_set_drvdata(pdev, ndev); mod = netdev_priv(ndev); mod->ndev = ndev; mod->num = pdata->modno; netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS); skb_queue_head_init(&mod->echoq); spin_lock_init(&mod->lock); init_completion(&mod->termination_comp); init_completion(&mod->buserror_comp); /* setup device-specific sysfs attributes */ ndev->sysfs_groups[0] = &ican3_sysfs_attr_group; /* the first unallocated page in the DPM is 9 */ mod->free_page = DPM_FREE_START; ndev->netdev_ops = &ican3_netdev_ops; ndev->flags |= IFF_ECHO; SET_NETDEV_DEV(ndev, &pdev->dev); mod->can.clock.freq = ICAN3_CAN_CLOCK; mod->can.bittiming_const = &ican3_bittiming_const; mod->can.do_set_mode = ican3_set_mode; mod->can.do_get_berr_counter = ican3_get_berr_counter; mod->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_ONE_SHOT; /* find our IRQ number */ mod->irq = platform_get_irq(pdev, 0); if (mod->irq < 0) { dev_err(dev, "IRQ line not found\n"); ret = -ENODEV; goto out_free_ndev; } ndev->irq = mod->irq; /* get access to the MODULbus registers for this module */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "MODULbus registers not found\n"); ret = -ENODEV; goto out_free_ndev; } mod->dpm = ioremap(res->start, resource_size(res)); if (!mod->dpm) { dev_err(dev, "MODULbus registers not ioremap\n"); ret = -ENOMEM; goto out_free_ndev; } mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE; /* get access to the control registers for this module */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { dev_err(dev, "CONTROL registers not found\n"); ret = -ENODEV; goto out_iounmap_dpm; } mod->ctrl = ioremap(res->start, resource_size(res)); if (!mod->ctrl) { dev_err(dev, "CONTROL registers not ioremap\n"); ret = -ENOMEM; goto out_iounmap_dpm; } /* disable our IRQ, then hookup the IRQ handler */ iowrite8(1 << mod->num, &mod->ctrl->int_disable); ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod); if (ret) { dev_err(dev, "unable to request IRQ\n"); goto out_iounmap_ctrl; } /* reset and initialize the CAN controller into fast mode */ napi_enable(&mod->napi); ret = ican3_startup_module(mod); if (ret) { dev_err(dev, "%s: unable to start CANdev\n", __func__); goto out_free_irq; } /* register with the Linux CAN layer */ ret = register_candev(ndev); if (ret) { dev_err(dev, "%s: unable to register CANdev\n", __func__); goto out_free_irq; } netdev_info(mod->ndev, "module %d: registered CAN device\n", pdata->modno); return 0; out_free_irq: napi_disable(&mod->napi); iowrite8(1 << mod->num, &mod->ctrl->int_disable); free_irq(mod->irq, mod); out_iounmap_ctrl: iounmap(mod->ctrl); out_iounmap_dpm: iounmap(mod->dpm); out_free_ndev: free_candev(ndev); out_return: return ret; } static int ican3_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct ican3_dev *mod = netdev_priv(ndev); /* unregister the netdevice, stop interrupts */ unregister_netdev(ndev); napi_disable(&mod->napi); iowrite8(1 << mod->num, &mod->ctrl->int_disable); free_irq(mod->irq, mod); /* put the module into reset */ ican3_shutdown_module(mod); /* unmap all registers */ iounmap(mod->ctrl); iounmap(mod->dpm); free_candev(ndev); return 0; } static struct platform_driver ican3_driver = { .driver = { .name = DRV_NAME, }, .probe = ican3_probe, .remove = ican3_remove, }; module_platform_driver(ican3_driver); MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:janz-ican3");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1