162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Janz MODULbus VMOD-ICAN3 CAN Interface Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/ethtool.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/netdevice.h> 1662306a36Sopenharmony_ci#include <linux/can.h> 1762306a36Sopenharmony_ci#include <linux/can/dev.h> 1862306a36Sopenharmony_ci#include <linux/can/skb.h> 1962306a36Sopenharmony_ci#include <linux/can/error.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/mfd/janz.h> 2262306a36Sopenharmony_ci#include <asm/io.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* the DPM has 64k of memory, organized into 256x 256 byte pages */ 2562306a36Sopenharmony_ci#define DPM_NUM_PAGES 256 2662306a36Sopenharmony_ci#define DPM_PAGE_SIZE 256 2762306a36Sopenharmony_ci#define DPM_PAGE_ADDR(p) ((p) * DPM_PAGE_SIZE) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* JANZ ICAN3 "old-style" host interface queue page numbers */ 3062306a36Sopenharmony_ci#define QUEUE_OLD_CONTROL 0 3162306a36Sopenharmony_ci#define QUEUE_OLD_RB0 1 3262306a36Sopenharmony_ci#define QUEUE_OLD_RB1 2 3362306a36Sopenharmony_ci#define QUEUE_OLD_WB0 3 3462306a36Sopenharmony_ci#define QUEUE_OLD_WB1 4 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Janz ICAN3 "old-style" host interface control registers */ 3762306a36Sopenharmony_ci#define MSYNC_PEER 0x00 /* ICAN only */ 3862306a36Sopenharmony_ci#define MSYNC_LOCL 0x01 /* host only */ 3962306a36Sopenharmony_ci#define TARGET_RUNNING 0x02 4062306a36Sopenharmony_ci#define FIRMWARE_STAMP 0x60 /* big endian firmware stamp */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define MSYNC_RB0 0x01 4362306a36Sopenharmony_ci#define MSYNC_RB1 0x02 4462306a36Sopenharmony_ci#define MSYNC_RBLW 0x04 4562306a36Sopenharmony_ci#define MSYNC_RB_MASK (MSYNC_RB0 | MSYNC_RB1) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define MSYNC_WB0 0x10 4862306a36Sopenharmony_ci#define MSYNC_WB1 0x20 4962306a36Sopenharmony_ci#define MSYNC_WBLW 0x40 5062306a36Sopenharmony_ci#define MSYNC_WB_MASK (MSYNC_WB0 | MSYNC_WB1) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Janz ICAN3 "new-style" host interface queue page numbers */ 5362306a36Sopenharmony_ci#define QUEUE_TOHOST 5 5462306a36Sopenharmony_ci#define QUEUE_FROMHOST_MID 6 5562306a36Sopenharmony_ci#define QUEUE_FROMHOST_HIGH 7 5662306a36Sopenharmony_ci#define QUEUE_FROMHOST_LOW 8 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* The first free page in the DPM is #9 */ 5962306a36Sopenharmony_ci#define DPM_FREE_START 9 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */ 6262306a36Sopenharmony_ci#define DESC_VALID 0x80 6362306a36Sopenharmony_ci#define DESC_WRAP 0x40 6462306a36Sopenharmony_ci#define DESC_INTERRUPT 0x20 6562306a36Sopenharmony_ci#define DESC_IVALID 0x10 6662306a36Sopenharmony_ci#define DESC_LEN(len) (len) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* Janz ICAN3 Firmware Messages */ 6962306a36Sopenharmony_ci#define MSG_CONNECTI 0x02 7062306a36Sopenharmony_ci#define MSG_DISCONNECT 0x03 7162306a36Sopenharmony_ci#define MSG_IDVERS 0x04 7262306a36Sopenharmony_ci#define MSG_MSGLOST 0x05 7362306a36Sopenharmony_ci#define MSG_NEWHOSTIF 0x08 7462306a36Sopenharmony_ci#define MSG_INQUIRY 0x0a 7562306a36Sopenharmony_ci#define MSG_SETAFILMASK 0x10 7662306a36Sopenharmony_ci#define MSG_INITFDPMQUEUE 0x11 7762306a36Sopenharmony_ci#define MSG_HWCONF 0x12 7862306a36Sopenharmony_ci#define MSG_FMSGLOST 0x15 7962306a36Sopenharmony_ci#define MSG_CEVTIND 0x37 8062306a36Sopenharmony_ci#define MSG_CBTRREQ 0x41 8162306a36Sopenharmony_ci#define MSG_COFFREQ 0x42 8262306a36Sopenharmony_ci#define MSG_CONREQ 0x43 8362306a36Sopenharmony_ci#define MSG_CCONFREQ 0x47 8462306a36Sopenharmony_ci#define MSG_NMTS 0xb0 8562306a36Sopenharmony_ci#define MSG_LMTS 0xb4 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * Janz ICAN3 CAN Inquiry Message Types 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * NOTE: there appears to be a firmware bug here. You must send 9162306a36Sopenharmony_ci * NOTE: INQUIRY_STATUS and expect to receive an INQUIRY_EXTENDED 9262306a36Sopenharmony_ci * NOTE: response. The controller never responds to a message with 9362306a36Sopenharmony_ci * NOTE: the INQUIRY_EXTENDED subspec :( 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci#define INQUIRY_STATUS 0x00 9662306a36Sopenharmony_ci#define INQUIRY_TERMINATION 0x01 9762306a36Sopenharmony_ci#define INQUIRY_EXTENDED 0x04 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */ 10062306a36Sopenharmony_ci#define SETAFILMASK_REJECT 0x00 10162306a36Sopenharmony_ci#define SETAFILMASK_FASTIF 0x02 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* Janz ICAN3 CAN Hardware Configuration Message Types */ 10462306a36Sopenharmony_ci#define HWCONF_TERMINATE_ON 0x01 10562306a36Sopenharmony_ci#define HWCONF_TERMINATE_OFF 0x00 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* Janz ICAN3 CAN Event Indication Message Types */ 10862306a36Sopenharmony_ci#define CEVTIND_EI 0x01 10962306a36Sopenharmony_ci#define CEVTIND_DOI 0x02 11062306a36Sopenharmony_ci#define CEVTIND_LOST 0x04 11162306a36Sopenharmony_ci#define CEVTIND_FULL 0x08 11262306a36Sopenharmony_ci#define CEVTIND_BEI 0x10 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define CEVTIND_CHIP_SJA1000 0x02 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define ICAN3_BUSERR_QUOTA_MAX 255 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* Janz ICAN3 CAN Frame Conversion */ 11962306a36Sopenharmony_ci#define ICAN3_SNGL 0x02 12062306a36Sopenharmony_ci#define ICAN3_ECHO 0x10 12162306a36Sopenharmony_ci#define ICAN3_EFF_RTR 0x40 12262306a36Sopenharmony_ci#define ICAN3_SFF_RTR 0x10 12362306a36Sopenharmony_ci#define ICAN3_EFF 0x80 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define ICAN3_CAN_TYPE_MASK 0x0f 12662306a36Sopenharmony_ci#define ICAN3_CAN_TYPE_SFF 0x00 12762306a36Sopenharmony_ci#define ICAN3_CAN_TYPE_EFF 0x01 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define ICAN3_CAN_DLC_MASK 0x0f 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* Janz ICAN3 NMTS subtypes */ 13262306a36Sopenharmony_ci#define NMTS_CREATE_NODE_REQ 0x0 13362306a36Sopenharmony_ci#define NMTS_SLAVE_STATE_IND 0x8 13462306a36Sopenharmony_ci#define NMTS_SLAVE_EVENT_IND 0x9 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* Janz ICAN3 LMTS subtypes */ 13762306a36Sopenharmony_ci#define LMTS_BUSON_REQ 0x0 13862306a36Sopenharmony_ci#define LMTS_BUSOFF_REQ 0x1 13962306a36Sopenharmony_ci#define LMTS_CAN_CONF_REQ 0x2 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* Janz ICAN3 NMTS Event indications */ 14262306a36Sopenharmony_ci#define NE_LOCAL_OCCURRED 0x3 14362306a36Sopenharmony_ci#define NE_LOCAL_RESOLVED 0x2 14462306a36Sopenharmony_ci#define NE_REMOTE_OCCURRED 0xc 14562306a36Sopenharmony_ci#define NE_REMOTE_RESOLVED 0x8 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* 14862306a36Sopenharmony_ci * SJA1000 Status and Error Register Definitions 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * Copied from drivers/net/can/sja1000/sja1000.h 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* status register content */ 15462306a36Sopenharmony_ci#define SR_BS 0x80 15562306a36Sopenharmony_ci#define SR_ES 0x40 15662306a36Sopenharmony_ci#define SR_TS 0x20 15762306a36Sopenharmony_ci#define SR_RS 0x10 15862306a36Sopenharmony_ci#define SR_TCS 0x08 15962306a36Sopenharmony_ci#define SR_TBS 0x04 16062306a36Sopenharmony_ci#define SR_DOS 0x02 16162306a36Sopenharmony_ci#define SR_RBS 0x01 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci#define SR_CRIT (SR_BS|SR_ES) 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* ECC register */ 16662306a36Sopenharmony_ci#define ECC_SEG 0x1F 16762306a36Sopenharmony_ci#define ECC_DIR 0x20 16862306a36Sopenharmony_ci#define ECC_ERR 6 16962306a36Sopenharmony_ci#define ECC_BIT 0x00 17062306a36Sopenharmony_ci#define ECC_FORM 0x40 17162306a36Sopenharmony_ci#define ECC_STUFF 0x80 17262306a36Sopenharmony_ci#define ECC_MASK 0xc0 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* Number of buffers for use in the "new-style" host interface */ 17562306a36Sopenharmony_ci#define ICAN3_NEW_BUFFERS 16 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* Number of buffers for use in the "fast" host interface */ 17862306a36Sopenharmony_ci#define ICAN3_TX_BUFFERS 512 17962306a36Sopenharmony_ci#define ICAN3_RX_BUFFERS 1024 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* SJA1000 Clock Input */ 18262306a36Sopenharmony_ci#define ICAN3_CAN_CLOCK 8000000 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* Janz ICAN3 firmware types */ 18562306a36Sopenharmony_cienum ican3_fwtype { 18662306a36Sopenharmony_ci ICAN3_FWTYPE_ICANOS, 18762306a36Sopenharmony_ci ICAN3_FWTYPE_CAL_CANOPEN, 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* Driver Name */ 19162306a36Sopenharmony_ci#define DRV_NAME "janz-ican3" 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */ 19462306a36Sopenharmony_cistruct ican3_dpm_control { 19562306a36Sopenharmony_ci /* window address register */ 19662306a36Sopenharmony_ci u8 window_address; 19762306a36Sopenharmony_ci u8 unused1; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* 20062306a36Sopenharmony_ci * Read access: clear interrupt from microcontroller 20162306a36Sopenharmony_ci * Write access: send interrupt to microcontroller 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci u8 interrupt; 20462306a36Sopenharmony_ci u8 unused2; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* write-only: reset all hardware on the module */ 20762306a36Sopenharmony_ci u8 hwreset; 20862306a36Sopenharmony_ci u8 unused3; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* write-only: generate an interrupt to the TPU */ 21162306a36Sopenharmony_ci u8 tpuinterrupt; 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistruct ican3_dev { 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* must be the first member */ 21762306a36Sopenharmony_ci struct can_priv can; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* CAN network device */ 22062306a36Sopenharmony_ci struct net_device *ndev; 22162306a36Sopenharmony_ci struct napi_struct napi; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* module number */ 22462306a36Sopenharmony_ci unsigned int num; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* base address of registers and IRQ */ 22762306a36Sopenharmony_ci struct janz_cmodio_onboard_regs __iomem *ctrl; 22862306a36Sopenharmony_ci struct ican3_dpm_control __iomem *dpmctrl; 22962306a36Sopenharmony_ci void __iomem *dpm; 23062306a36Sopenharmony_ci int irq; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* CAN bus termination status */ 23362306a36Sopenharmony_ci struct completion termination_comp; 23462306a36Sopenharmony_ci bool termination_enabled; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* CAN bus error status registers */ 23762306a36Sopenharmony_ci struct completion buserror_comp; 23862306a36Sopenharmony_ci struct can_berr_counter bec; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* firmware type */ 24162306a36Sopenharmony_ci enum ican3_fwtype fwtype; 24262306a36Sopenharmony_ci char fwinfo[32]; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* old and new style host interface */ 24562306a36Sopenharmony_ci unsigned int iftype; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* queue for echo packets */ 24862306a36Sopenharmony_ci struct sk_buff_head echoq; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * Any function which changes the current DPM page must hold this 25262306a36Sopenharmony_ci * lock while it is performing data accesses. This ensures that the 25362306a36Sopenharmony_ci * function will not be preempted and end up reading data from a 25462306a36Sopenharmony_ci * different DPM page than it expects. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci spinlock_t lock; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* new host interface */ 25962306a36Sopenharmony_ci unsigned int rx_int; 26062306a36Sopenharmony_ci unsigned int rx_num; 26162306a36Sopenharmony_ci unsigned int tx_num; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* fast host interface */ 26462306a36Sopenharmony_ci unsigned int fastrx_start; 26562306a36Sopenharmony_ci unsigned int fastrx_num; 26662306a36Sopenharmony_ci unsigned int fasttx_start; 26762306a36Sopenharmony_ci unsigned int fasttx_num; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* first free DPM page */ 27062306a36Sopenharmony_ci unsigned int free_page; 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistruct ican3_msg { 27462306a36Sopenharmony_ci u8 control; 27562306a36Sopenharmony_ci u8 spec; 27662306a36Sopenharmony_ci __le16 len; 27762306a36Sopenharmony_ci u8 data[252]; 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistruct ican3_new_desc { 28162306a36Sopenharmony_ci u8 control; 28262306a36Sopenharmony_ci u8 pointer; 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistruct ican3_fast_desc { 28662306a36Sopenharmony_ci u8 control; 28762306a36Sopenharmony_ci u8 command; 28862306a36Sopenharmony_ci u8 data[14]; 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/* write to the window basic address register */ 29262306a36Sopenharmony_cistatic inline void ican3_set_page(struct ican3_dev *mod, unsigned int page) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci BUG_ON(page >= DPM_NUM_PAGES); 29562306a36Sopenharmony_ci iowrite8(page, &mod->dpmctrl->window_address); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* 29962306a36Sopenharmony_ci * ICAN3 "old-style" host interface 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* 30362306a36Sopenharmony_ci * Receive a message from the ICAN3 "old-style" firmware interface 30462306a36Sopenharmony_ci * 30562306a36Sopenharmony_ci * LOCKING: must hold mod->lock 30662306a36Sopenharmony_ci * 30762306a36Sopenharmony_ci * returns 0 on success, -ENOMEM when no message exists 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci unsigned int mbox, mbox_page; 31262306a36Sopenharmony_ci u8 locl, peer, xord; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* get the MSYNC registers */ 31562306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_OLD_CONTROL); 31662306a36Sopenharmony_ci peer = ioread8(mod->dpm + MSYNC_PEER); 31762306a36Sopenharmony_ci locl = ioread8(mod->dpm + MSYNC_LOCL); 31862306a36Sopenharmony_ci xord = locl ^ peer; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if ((xord & MSYNC_RB_MASK) == 0x00) { 32162306a36Sopenharmony_ci netdev_dbg(mod->ndev, "no mbox for reading\n"); 32262306a36Sopenharmony_ci return -ENOMEM; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* find the first free mbox to read */ 32662306a36Sopenharmony_ci if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK) 32762306a36Sopenharmony_ci mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1; 32862306a36Sopenharmony_ci else 32962306a36Sopenharmony_ci mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* copy the message */ 33262306a36Sopenharmony_ci mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1; 33362306a36Sopenharmony_ci ican3_set_page(mod, mbox_page); 33462306a36Sopenharmony_ci memcpy_fromio(msg, mod->dpm, sizeof(*msg)); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * notify the firmware that the read buffer is available 33862306a36Sopenharmony_ci * for it to fill again 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci locl ^= mbox; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_OLD_CONTROL); 34362306a36Sopenharmony_ci iowrite8(locl, mod->dpm + MSYNC_LOCL); 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/* 34862306a36Sopenharmony_ci * Send a message through the "old-style" firmware interface 34962306a36Sopenharmony_ci * 35062306a36Sopenharmony_ci * LOCKING: must hold mod->lock 35162306a36Sopenharmony_ci * 35262306a36Sopenharmony_ci * returns 0 on success, -ENOMEM when no free space exists 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistatic int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci unsigned int mbox, mbox_page; 35762306a36Sopenharmony_ci u8 locl, peer, xord; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* get the MSYNC registers */ 36062306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_OLD_CONTROL); 36162306a36Sopenharmony_ci peer = ioread8(mod->dpm + MSYNC_PEER); 36262306a36Sopenharmony_ci locl = ioread8(mod->dpm + MSYNC_LOCL); 36362306a36Sopenharmony_ci xord = locl ^ peer; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) { 36662306a36Sopenharmony_ci netdev_err(mod->ndev, "no mbox for writing\n"); 36762306a36Sopenharmony_ci return -ENOMEM; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* calculate a free mbox to use */ 37162306a36Sopenharmony_ci mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* copy the message to the DPM */ 37462306a36Sopenharmony_ci mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1; 37562306a36Sopenharmony_ci ican3_set_page(mod, mbox_page); 37662306a36Sopenharmony_ci memcpy_toio(mod->dpm, msg, sizeof(*msg)); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci locl ^= mbox; 37962306a36Sopenharmony_ci if (mbox == MSYNC_WB1) 38062306a36Sopenharmony_ci locl |= MSYNC_WBLW; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_OLD_CONTROL); 38362306a36Sopenharmony_ci iowrite8(locl, mod->dpm + MSYNC_LOCL); 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * ICAN3 "new-style" Host Interface Setup 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void ican3_init_new_host_interface(struct ican3_dev *mod) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct ican3_new_desc desc; 39462306a36Sopenharmony_ci unsigned long flags; 39562306a36Sopenharmony_ci void __iomem *dst; 39662306a36Sopenharmony_ci int i; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci spin_lock_irqsave(&mod->lock, flags); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* setup the internal datastructures for RX */ 40162306a36Sopenharmony_ci mod->rx_num = 0; 40262306a36Sopenharmony_ci mod->rx_int = 0; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* tohost queue descriptors are in page 5 */ 40562306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_TOHOST); 40662306a36Sopenharmony_ci dst = mod->dpm; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* initialize the tohost (rx) queue descriptors: pages 9-24 */ 40962306a36Sopenharmony_ci for (i = 0; i < ICAN3_NEW_BUFFERS; i++) { 41062306a36Sopenharmony_ci desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */ 41162306a36Sopenharmony_ci desc.pointer = mod->free_page; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* set wrap flag on last buffer */ 41462306a36Sopenharmony_ci if (i == ICAN3_NEW_BUFFERS - 1) 41562306a36Sopenharmony_ci desc.control |= DESC_WRAP; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci memcpy_toio(dst, &desc, sizeof(desc)); 41862306a36Sopenharmony_ci dst += sizeof(desc); 41962306a36Sopenharmony_ci mod->free_page++; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* fromhost (tx) mid queue descriptors are in page 6 */ 42362306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_FROMHOST_MID); 42462306a36Sopenharmony_ci dst = mod->dpm; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* setup the internal datastructures for TX */ 42762306a36Sopenharmony_ci mod->tx_num = 0; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* initialize the fromhost mid queue descriptors: pages 25-40 */ 43062306a36Sopenharmony_ci for (i = 0; i < ICAN3_NEW_BUFFERS; i++) { 43162306a36Sopenharmony_ci desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */ 43262306a36Sopenharmony_ci desc.pointer = mod->free_page; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* set wrap flag on last buffer */ 43562306a36Sopenharmony_ci if (i == ICAN3_NEW_BUFFERS - 1) 43662306a36Sopenharmony_ci desc.control |= DESC_WRAP; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci memcpy_toio(dst, &desc, sizeof(desc)); 43962306a36Sopenharmony_ci dst += sizeof(desc); 44062306a36Sopenharmony_ci mod->free_page++; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* fromhost hi queue descriptors are in page 7 */ 44462306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_FROMHOST_HIGH); 44562306a36Sopenharmony_ci dst = mod->dpm; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* initialize only a single buffer in the fromhost hi queue (unused) */ 44862306a36Sopenharmony_ci desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */ 44962306a36Sopenharmony_ci desc.pointer = mod->free_page; 45062306a36Sopenharmony_ci memcpy_toio(dst, &desc, sizeof(desc)); 45162306a36Sopenharmony_ci mod->free_page++; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* fromhost low queue descriptors are in page 8 */ 45462306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_FROMHOST_LOW); 45562306a36Sopenharmony_ci dst = mod->dpm; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* initialize only a single buffer in the fromhost low queue (unused) */ 45862306a36Sopenharmony_ci desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */ 45962306a36Sopenharmony_ci desc.pointer = mod->free_page; 46062306a36Sopenharmony_ci memcpy_toio(dst, &desc, sizeof(desc)); 46162306a36Sopenharmony_ci mod->free_page++; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci spin_unlock_irqrestore(&mod->lock, flags); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* 46762306a36Sopenharmony_ci * ICAN3 Fast Host Interface Setup 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic void ican3_init_fast_host_interface(struct ican3_dev *mod) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct ican3_fast_desc desc; 47362306a36Sopenharmony_ci unsigned long flags; 47462306a36Sopenharmony_ci unsigned int addr; 47562306a36Sopenharmony_ci void __iomem *dst; 47662306a36Sopenharmony_ci int i; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci spin_lock_irqsave(&mod->lock, flags); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* save the start recv page */ 48162306a36Sopenharmony_ci mod->fastrx_start = mod->free_page; 48262306a36Sopenharmony_ci mod->fastrx_num = 0; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* build a single fast tohost queue descriptor */ 48562306a36Sopenharmony_ci memset(&desc, 0, sizeof(desc)); 48662306a36Sopenharmony_ci desc.control = 0x00; 48762306a36Sopenharmony_ci desc.command = 1; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* build the tohost queue descriptor ring in memory */ 49062306a36Sopenharmony_ci addr = 0; 49162306a36Sopenharmony_ci for (i = 0; i < ICAN3_RX_BUFFERS; i++) { 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* set the wrap bit on the last buffer */ 49462306a36Sopenharmony_ci if (i == ICAN3_RX_BUFFERS - 1) 49562306a36Sopenharmony_ci desc.control |= DESC_WRAP; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* switch to the correct page */ 49862306a36Sopenharmony_ci ican3_set_page(mod, mod->free_page); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* copy the descriptor to the DPM */ 50162306a36Sopenharmony_ci dst = mod->dpm + addr; 50262306a36Sopenharmony_ci memcpy_toio(dst, &desc, sizeof(desc)); 50362306a36Sopenharmony_ci addr += sizeof(desc); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* move to the next page if necessary */ 50662306a36Sopenharmony_ci if (addr >= DPM_PAGE_SIZE) { 50762306a36Sopenharmony_ci addr = 0; 50862306a36Sopenharmony_ci mod->free_page++; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* make sure we page-align the next queue */ 51362306a36Sopenharmony_ci if (addr != 0) 51462306a36Sopenharmony_ci mod->free_page++; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* save the start xmit page */ 51762306a36Sopenharmony_ci mod->fasttx_start = mod->free_page; 51862306a36Sopenharmony_ci mod->fasttx_num = 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* build a single fast fromhost queue descriptor */ 52162306a36Sopenharmony_ci memset(&desc, 0, sizeof(desc)); 52262306a36Sopenharmony_ci desc.control = DESC_VALID; 52362306a36Sopenharmony_ci desc.command = 1; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* build the fromhost queue descriptor ring in memory */ 52662306a36Sopenharmony_ci addr = 0; 52762306a36Sopenharmony_ci for (i = 0; i < ICAN3_TX_BUFFERS; i++) { 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* set the wrap bit on the last buffer */ 53062306a36Sopenharmony_ci if (i == ICAN3_TX_BUFFERS - 1) 53162306a36Sopenharmony_ci desc.control |= DESC_WRAP; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* switch to the correct page */ 53462306a36Sopenharmony_ci ican3_set_page(mod, mod->free_page); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* copy the descriptor to the DPM */ 53762306a36Sopenharmony_ci dst = mod->dpm + addr; 53862306a36Sopenharmony_ci memcpy_toio(dst, &desc, sizeof(desc)); 53962306a36Sopenharmony_ci addr += sizeof(desc); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* move to the next page if necessary */ 54262306a36Sopenharmony_ci if (addr >= DPM_PAGE_SIZE) { 54362306a36Sopenharmony_ci addr = 0; 54462306a36Sopenharmony_ci mod->free_page++; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci spin_unlock_irqrestore(&mod->lock, flags); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/* 55262306a36Sopenharmony_ci * ICAN3 "new-style" Host Interface Message Helpers 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci/* 55662306a36Sopenharmony_ci * LOCKING: must hold mod->lock 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_cistatic int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct ican3_new_desc desc; 56162306a36Sopenharmony_ci void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc)); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* switch to the fromhost mid queue, and read the buffer descriptor */ 56462306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_FROMHOST_MID); 56562306a36Sopenharmony_ci memcpy_fromio(&desc, desc_addr, sizeof(desc)); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!(desc.control & DESC_VALID)) { 56862306a36Sopenharmony_ci netdev_dbg(mod->ndev, "%s: no free buffers\n", __func__); 56962306a36Sopenharmony_ci return -ENOMEM; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* switch to the data page, copy the data */ 57362306a36Sopenharmony_ci ican3_set_page(mod, desc.pointer); 57462306a36Sopenharmony_ci memcpy_toio(mod->dpm, msg, sizeof(*msg)); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* switch back to the descriptor, set the valid bit, write it back */ 57762306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_FROMHOST_MID); 57862306a36Sopenharmony_ci desc.control ^= DESC_VALID; 57962306a36Sopenharmony_ci memcpy_toio(desc_addr, &desc, sizeof(desc)); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* update the tx number */ 58262306a36Sopenharmony_ci mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1); 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci/* 58762306a36Sopenharmony_ci * LOCKING: must hold mod->lock 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_cistatic int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct ican3_new_desc desc; 59262306a36Sopenharmony_ci void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc)); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* switch to the tohost queue, and read the buffer descriptor */ 59562306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_TOHOST); 59662306a36Sopenharmony_ci memcpy_fromio(&desc, desc_addr, sizeof(desc)); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (!(desc.control & DESC_VALID)) { 59962306a36Sopenharmony_ci netdev_dbg(mod->ndev, "%s: no buffers to recv\n", __func__); 60062306a36Sopenharmony_ci return -ENOMEM; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* switch to the data page, copy the data */ 60462306a36Sopenharmony_ci ican3_set_page(mod, desc.pointer); 60562306a36Sopenharmony_ci memcpy_fromio(msg, mod->dpm, sizeof(*msg)); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* switch back to the descriptor, toggle the valid bit, write it back */ 60862306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_TOHOST); 60962306a36Sopenharmony_ci desc.control ^= DESC_VALID; 61062306a36Sopenharmony_ci memcpy_toio(desc_addr, &desc, sizeof(desc)); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* update the rx number */ 61362306a36Sopenharmony_ci mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1); 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/* 61862306a36Sopenharmony_ci * Message Send / Recv Helpers 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci unsigned long flags; 62462306a36Sopenharmony_ci int ret; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci spin_lock_irqsave(&mod->lock, flags); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (mod->iftype == 0) 62962306a36Sopenharmony_ci ret = ican3_old_send_msg(mod, msg); 63062306a36Sopenharmony_ci else 63162306a36Sopenharmony_ci ret = ican3_new_send_msg(mod, msg); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci spin_unlock_irqrestore(&mod->lock, flags); 63462306a36Sopenharmony_ci return ret; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci unsigned long flags; 64062306a36Sopenharmony_ci int ret; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci spin_lock_irqsave(&mod->lock, flags); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (mod->iftype == 0) 64562306a36Sopenharmony_ci ret = ican3_old_recv_msg(mod, msg); 64662306a36Sopenharmony_ci else 64762306a36Sopenharmony_ci ret = ican3_new_recv_msg(mod, msg); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci spin_unlock_irqrestore(&mod->lock, flags); 65062306a36Sopenharmony_ci return ret; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/* 65462306a36Sopenharmony_ci * Quick Pre-constructed Messages 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic int ican3_msg_connect(struct ican3_dev *mod) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct ican3_msg msg; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 66262306a36Sopenharmony_ci msg.spec = MSG_CONNECTI; 66362306a36Sopenharmony_ci msg.len = cpu_to_le16(0); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return ican3_send_msg(mod, &msg); 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int ican3_msg_disconnect(struct ican3_dev *mod) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct ican3_msg msg; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 67362306a36Sopenharmony_ci msg.spec = MSG_DISCONNECT; 67462306a36Sopenharmony_ci msg.len = cpu_to_le16(0); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return ican3_send_msg(mod, &msg); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic int ican3_msg_newhostif(struct ican3_dev *mod) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct ican3_msg msg; 68262306a36Sopenharmony_ci int ret; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 68562306a36Sopenharmony_ci msg.spec = MSG_NEWHOSTIF; 68662306a36Sopenharmony_ci msg.len = cpu_to_le16(0); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* If we're not using the old interface, switching seems bogus */ 68962306a36Sopenharmony_ci WARN_ON(mod->iftype != 0); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci ret = ican3_send_msg(mod, &msg); 69262306a36Sopenharmony_ci if (ret) 69362306a36Sopenharmony_ci return ret; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* mark the module as using the new host interface */ 69662306a36Sopenharmony_ci mod->iftype = 1; 69762306a36Sopenharmony_ci return 0; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int ican3_msg_fasthostif(struct ican3_dev *mod) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct ican3_msg msg; 70362306a36Sopenharmony_ci unsigned int addr; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 70662306a36Sopenharmony_ci msg.spec = MSG_INITFDPMQUEUE; 70762306a36Sopenharmony_ci msg.len = cpu_to_le16(8); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* write the tohost queue start address */ 71062306a36Sopenharmony_ci addr = DPM_PAGE_ADDR(mod->fastrx_start); 71162306a36Sopenharmony_ci msg.data[0] = addr & 0xff; 71262306a36Sopenharmony_ci msg.data[1] = (addr >> 8) & 0xff; 71362306a36Sopenharmony_ci msg.data[2] = (addr >> 16) & 0xff; 71462306a36Sopenharmony_ci msg.data[3] = (addr >> 24) & 0xff; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* write the fromhost queue start address */ 71762306a36Sopenharmony_ci addr = DPM_PAGE_ADDR(mod->fasttx_start); 71862306a36Sopenharmony_ci msg.data[4] = addr & 0xff; 71962306a36Sopenharmony_ci msg.data[5] = (addr >> 8) & 0xff; 72062306a36Sopenharmony_ci msg.data[6] = (addr >> 16) & 0xff; 72162306a36Sopenharmony_ci msg.data[7] = (addr >> 24) & 0xff; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* If we're not using the new interface yet, we cannot do this */ 72462306a36Sopenharmony_ci WARN_ON(mod->iftype != 1); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return ican3_send_msg(mod, &msg); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci/* 73062306a36Sopenharmony_ci * Setup the CAN filter to either accept or reject all 73162306a36Sopenharmony_ci * messages from the CAN bus. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_cistatic int ican3_set_id_filter(struct ican3_dev *mod, bool accept) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct ican3_msg msg; 73662306a36Sopenharmony_ci int ret; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Standard Frame Format */ 73962306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 74062306a36Sopenharmony_ci msg.spec = MSG_SETAFILMASK; 74162306a36Sopenharmony_ci msg.len = cpu_to_le16(5); 74262306a36Sopenharmony_ci msg.data[0] = 0x00; /* IDLo LSB */ 74362306a36Sopenharmony_ci msg.data[1] = 0x00; /* IDLo MSB */ 74462306a36Sopenharmony_ci msg.data[2] = 0xff; /* IDHi LSB */ 74562306a36Sopenharmony_ci msg.data[3] = 0x07; /* IDHi MSB */ 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* accept all frames for fast host if, or reject all frames */ 74862306a36Sopenharmony_ci msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci ret = ican3_send_msg(mod, &msg); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci return ret; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Extended Frame Format */ 75562306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 75662306a36Sopenharmony_ci msg.spec = MSG_SETAFILMASK; 75762306a36Sopenharmony_ci msg.len = cpu_to_le16(13); 75862306a36Sopenharmony_ci msg.data[0] = 0; /* MUX = 0 */ 75962306a36Sopenharmony_ci msg.data[1] = 0x00; /* IDLo LSB */ 76062306a36Sopenharmony_ci msg.data[2] = 0x00; 76162306a36Sopenharmony_ci msg.data[3] = 0x00; 76262306a36Sopenharmony_ci msg.data[4] = 0x20; /* IDLo MSB */ 76362306a36Sopenharmony_ci msg.data[5] = 0xff; /* IDHi LSB */ 76462306a36Sopenharmony_ci msg.data[6] = 0xff; 76562306a36Sopenharmony_ci msg.data[7] = 0xff; 76662306a36Sopenharmony_ci msg.data[8] = 0x3f; /* IDHi MSB */ 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* accept all frames for fast host if, or reject all frames */ 76962306a36Sopenharmony_ci msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return ican3_send_msg(mod, &msg); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/* 77562306a36Sopenharmony_ci * Bring the CAN bus online or offline 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_cistatic int ican3_set_bus_state(struct ican3_dev *mod, bool on) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct can_bittiming *bt = &mod->can.bittiming; 78062306a36Sopenharmony_ci struct ican3_msg msg; 78162306a36Sopenharmony_ci u8 btr0, btr1; 78262306a36Sopenharmony_ci int res; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* This algorithm was stolen from drivers/net/can/sja1000/sja1000.c */ 78562306a36Sopenharmony_ci /* The bittiming register command for the ICAN3 just sets the bit timing */ 78662306a36Sopenharmony_ci /* registers on the SJA1000 chip directly */ 78762306a36Sopenharmony_ci btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); 78862306a36Sopenharmony_ci btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | 78962306a36Sopenharmony_ci (((bt->phase_seg2 - 1) & 0x7) << 4); 79062306a36Sopenharmony_ci if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) 79162306a36Sopenharmony_ci btr1 |= 0x80; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (mod->fwtype == ICAN3_FWTYPE_ICANOS) { 79462306a36Sopenharmony_ci if (on) { 79562306a36Sopenharmony_ci /* set bittiming */ 79662306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 79762306a36Sopenharmony_ci msg.spec = MSG_CBTRREQ; 79862306a36Sopenharmony_ci msg.len = cpu_to_le16(4); 79962306a36Sopenharmony_ci msg.data[0] = 0x00; 80062306a36Sopenharmony_ci msg.data[1] = 0x00; 80162306a36Sopenharmony_ci msg.data[2] = btr0; 80262306a36Sopenharmony_ci msg.data[3] = btr1; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci res = ican3_send_msg(mod, &msg); 80562306a36Sopenharmony_ci if (res) 80662306a36Sopenharmony_ci return res; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* can-on/off request */ 81062306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 81162306a36Sopenharmony_ci msg.spec = on ? MSG_CONREQ : MSG_COFFREQ; 81262306a36Sopenharmony_ci msg.len = cpu_to_le16(0); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return ican3_send_msg(mod, &msg); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci } else if (mod->fwtype == ICAN3_FWTYPE_CAL_CANOPEN) { 81762306a36Sopenharmony_ci /* bittiming + can-on/off request */ 81862306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 81962306a36Sopenharmony_ci msg.spec = MSG_LMTS; 82062306a36Sopenharmony_ci if (on) { 82162306a36Sopenharmony_ci msg.len = cpu_to_le16(4); 82262306a36Sopenharmony_ci msg.data[0] = LMTS_BUSON_REQ; 82362306a36Sopenharmony_ci msg.data[1] = 0; 82462306a36Sopenharmony_ci msg.data[2] = btr0; 82562306a36Sopenharmony_ci msg.data[3] = btr1; 82662306a36Sopenharmony_ci } else { 82762306a36Sopenharmony_ci msg.len = cpu_to_le16(2); 82862306a36Sopenharmony_ci msg.data[0] = LMTS_BUSOFF_REQ; 82962306a36Sopenharmony_ci msg.data[1] = 0; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci res = ican3_send_msg(mod, &msg); 83262306a36Sopenharmony_ci if (res) 83362306a36Sopenharmony_ci return res; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (on) { 83662306a36Sopenharmony_ci /* create NMT Slave Node for error processing 83762306a36Sopenharmony_ci * class 2 (with error capability, see CiA/DS203-1) 83862306a36Sopenharmony_ci * id 1 83962306a36Sopenharmony_ci * name locnod1 (must be exactly 7 bytes) 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 84262306a36Sopenharmony_ci msg.spec = MSG_NMTS; 84362306a36Sopenharmony_ci msg.len = cpu_to_le16(11); 84462306a36Sopenharmony_ci msg.data[0] = NMTS_CREATE_NODE_REQ; 84562306a36Sopenharmony_ci msg.data[1] = 0; 84662306a36Sopenharmony_ci msg.data[2] = 2; /* node class */ 84762306a36Sopenharmony_ci msg.data[3] = 1; /* node id */ 84862306a36Sopenharmony_ci strcpy(msg.data + 4, "locnod1"); /* node name */ 84962306a36Sopenharmony_ci return ican3_send_msg(mod, &msg); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci return 0; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci return -ENOTSUPP; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic int ican3_set_termination(struct ican3_dev *mod, bool on) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct ican3_msg msg; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 86162306a36Sopenharmony_ci msg.spec = MSG_HWCONF; 86262306a36Sopenharmony_ci msg.len = cpu_to_le16(2); 86362306a36Sopenharmony_ci msg.data[0] = 0x00; 86462306a36Sopenharmony_ci msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return ican3_send_msg(mod, &msg); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic int ican3_send_inquiry(struct ican3_dev *mod, u8 subspec) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct ican3_msg msg; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 87462306a36Sopenharmony_ci msg.spec = MSG_INQUIRY; 87562306a36Sopenharmony_ci msg.len = cpu_to_le16(2); 87662306a36Sopenharmony_ci msg.data[0] = subspec; 87762306a36Sopenharmony_ci msg.data[1] = 0x00; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return ican3_send_msg(mod, &msg); 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic int ican3_set_buserror(struct ican3_dev *mod, u8 quota) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci struct ican3_msg msg; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (mod->fwtype == ICAN3_FWTYPE_ICANOS) { 88762306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 88862306a36Sopenharmony_ci msg.spec = MSG_CCONFREQ; 88962306a36Sopenharmony_ci msg.len = cpu_to_le16(2); 89062306a36Sopenharmony_ci msg.data[0] = 0x00; 89162306a36Sopenharmony_ci msg.data[1] = quota; 89262306a36Sopenharmony_ci } else if (mod->fwtype == ICAN3_FWTYPE_CAL_CANOPEN) { 89362306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 89462306a36Sopenharmony_ci msg.spec = MSG_LMTS; 89562306a36Sopenharmony_ci msg.len = cpu_to_le16(4); 89662306a36Sopenharmony_ci msg.data[0] = LMTS_CAN_CONF_REQ; 89762306a36Sopenharmony_ci msg.data[1] = 0x00; 89862306a36Sopenharmony_ci msg.data[2] = 0x00; 89962306a36Sopenharmony_ci msg.data[3] = quota; 90062306a36Sopenharmony_ci } else { 90162306a36Sopenharmony_ci return -ENOTSUPP; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci return ican3_send_msg(mod, &msg); 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci/* 90762306a36Sopenharmony_ci * ICAN3 to Linux CAN Frame Conversion 90862306a36Sopenharmony_ci */ 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic void ican3_to_can_frame(struct ican3_dev *mod, 91162306a36Sopenharmony_ci struct ican3_fast_desc *desc, 91262306a36Sopenharmony_ci struct can_frame *cf) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) { 91562306a36Sopenharmony_ci if (desc->data[1] & ICAN3_SFF_RTR) 91662306a36Sopenharmony_ci cf->can_id |= CAN_RTR_FLAG; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci cf->can_id |= desc->data[0] << 3; 91962306a36Sopenharmony_ci cf->can_id |= (desc->data[1] & 0xe0) >> 5; 92062306a36Sopenharmony_ci cf->len = can_cc_dlc2len(desc->data[1] & ICAN3_CAN_DLC_MASK); 92162306a36Sopenharmony_ci memcpy(cf->data, &desc->data[2], cf->len); 92262306a36Sopenharmony_ci } else { 92362306a36Sopenharmony_ci cf->len = can_cc_dlc2len(desc->data[0] & ICAN3_CAN_DLC_MASK); 92462306a36Sopenharmony_ci if (desc->data[0] & ICAN3_EFF_RTR) 92562306a36Sopenharmony_ci cf->can_id |= CAN_RTR_FLAG; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (desc->data[0] & ICAN3_EFF) { 92862306a36Sopenharmony_ci cf->can_id |= CAN_EFF_FLAG; 92962306a36Sopenharmony_ci cf->can_id |= desc->data[2] << 21; /* 28-21 */ 93062306a36Sopenharmony_ci cf->can_id |= desc->data[3] << 13; /* 20-13 */ 93162306a36Sopenharmony_ci cf->can_id |= desc->data[4] << 5; /* 12-5 */ 93262306a36Sopenharmony_ci cf->can_id |= (desc->data[5] & 0xf8) >> 3; 93362306a36Sopenharmony_ci } else { 93462306a36Sopenharmony_ci cf->can_id |= desc->data[2] << 3; /* 10-3 */ 93562306a36Sopenharmony_ci cf->can_id |= desc->data[3] >> 5; /* 2-0 */ 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci memcpy(cf->data, &desc->data[6], cf->len); 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic void can_frame_to_ican3(struct ican3_dev *mod, 94362306a36Sopenharmony_ci struct can_frame *cf, 94462306a36Sopenharmony_ci struct ican3_fast_desc *desc) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci /* clear out any stale data in the descriptor */ 94762306a36Sopenharmony_ci memset(desc->data, 0, sizeof(desc->data)); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* we always use the extended format, with the ECHO flag set */ 95062306a36Sopenharmony_ci desc->command = ICAN3_CAN_TYPE_EFF; 95162306a36Sopenharmony_ci desc->data[0] |= cf->len; 95262306a36Sopenharmony_ci desc->data[1] |= ICAN3_ECHO; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* support single transmission (no retries) mode */ 95562306a36Sopenharmony_ci if (mod->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) 95662306a36Sopenharmony_ci desc->data[1] |= ICAN3_SNGL; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (cf->can_id & CAN_RTR_FLAG) 95962306a36Sopenharmony_ci desc->data[0] |= ICAN3_EFF_RTR; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* pack the id into the correct places */ 96262306a36Sopenharmony_ci if (cf->can_id & CAN_EFF_FLAG) { 96362306a36Sopenharmony_ci desc->data[0] |= ICAN3_EFF; 96462306a36Sopenharmony_ci desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */ 96562306a36Sopenharmony_ci desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */ 96662306a36Sopenharmony_ci desc->data[4] = (cf->can_id & 0x00001fe0) >> 5; /* 12-5 */ 96762306a36Sopenharmony_ci desc->data[5] = (cf->can_id & 0x0000001f) << 3; /* 4-0 */ 96862306a36Sopenharmony_ci } else { 96962306a36Sopenharmony_ci desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */ 97062306a36Sopenharmony_ci desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0 */ 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* copy the data bits into the descriptor */ 97462306a36Sopenharmony_ci memcpy(&desc->data[6], cf->data, cf->len); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci/* 97862306a36Sopenharmony_ci * Interrupt Handling 97962306a36Sopenharmony_ci */ 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci/* 98262306a36Sopenharmony_ci * Handle an ID + Version message response from the firmware. We never generate 98362306a36Sopenharmony_ci * this message in production code, but it is very useful when debugging to be 98462306a36Sopenharmony_ci * able to display this message. 98562306a36Sopenharmony_ci */ 98662306a36Sopenharmony_cistatic void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci netdev_dbg(mod->ndev, "IDVERS response: %s\n", msg->data); 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct net_device *dev = mod->ndev; 99462306a36Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 99562306a36Sopenharmony_ci struct can_frame *cf; 99662306a36Sopenharmony_ci struct sk_buff *skb; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* 99962306a36Sopenharmony_ci * Report that communication messages with the microcontroller firmware 100062306a36Sopenharmony_ci * are being lost. These are never CAN frames, so we do not generate an 100162306a36Sopenharmony_ci * error frame for userspace 100262306a36Sopenharmony_ci */ 100362306a36Sopenharmony_ci if (msg->spec == MSG_MSGLOST) { 100462306a36Sopenharmony_ci netdev_err(mod->ndev, "lost %d control messages\n", msg->data[0]); 100562306a36Sopenharmony_ci return; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci /* 100962306a36Sopenharmony_ci * Oops, this indicates that we have lost messages in the fast queue, 101062306a36Sopenharmony_ci * which are exclusively CAN messages. Our driver isn't reading CAN 101162306a36Sopenharmony_ci * frames fast enough. 101262306a36Sopenharmony_ci * 101362306a36Sopenharmony_ci * We'll pretend that the SJA1000 told us that it ran out of buffer 101462306a36Sopenharmony_ci * space, because there is not a better message for this. 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_ci skb = alloc_can_err_skb(dev, &cf); 101762306a36Sopenharmony_ci if (skb) { 101862306a36Sopenharmony_ci cf->can_id |= CAN_ERR_CRTL; 101962306a36Sopenharmony_ci cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; 102062306a36Sopenharmony_ci stats->rx_over_errors++; 102162306a36Sopenharmony_ci stats->rx_errors++; 102262306a36Sopenharmony_ci netif_rx(skb); 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci/* 102762306a36Sopenharmony_ci * Handle CAN Event Indication Messages from the firmware 102862306a36Sopenharmony_ci * 102962306a36Sopenharmony_ci * The ICAN3 firmware provides the values of some SJA1000 registers when it 103062306a36Sopenharmony_ci * generates this message. The code below is largely copied from the 103162306a36Sopenharmony_ci * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_cistatic int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct net_device *dev = mod->ndev; 103662306a36Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 103762306a36Sopenharmony_ci enum can_state state = mod->can.state; 103862306a36Sopenharmony_ci u8 isrc, ecc, status, rxerr, txerr; 103962306a36Sopenharmony_ci struct can_frame *cf; 104062306a36Sopenharmony_ci struct sk_buff *skb; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* we can only handle the SJA1000 part */ 104362306a36Sopenharmony_ci if (msg->data[1] != CEVTIND_CHIP_SJA1000) { 104462306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to handle errors on non-SJA1000\n"); 104562306a36Sopenharmony_ci return -ENODEV; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* check the message length for sanity */ 104962306a36Sopenharmony_ci if (le16_to_cpu(msg->len) < 6) { 105062306a36Sopenharmony_ci netdev_err(mod->ndev, "error message too short\n"); 105162306a36Sopenharmony_ci return -EINVAL; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci isrc = msg->data[0]; 105562306a36Sopenharmony_ci ecc = msg->data[2]; 105662306a36Sopenharmony_ci status = msg->data[3]; 105762306a36Sopenharmony_ci rxerr = msg->data[4]; 105862306a36Sopenharmony_ci txerr = msg->data[5]; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* 106162306a36Sopenharmony_ci * This hardware lacks any support other than bus error messages to 106262306a36Sopenharmony_ci * determine if packet transmission has failed. 106362306a36Sopenharmony_ci * 106462306a36Sopenharmony_ci * When TX errors happen, one echo skb needs to be dropped from the 106562306a36Sopenharmony_ci * front of the queue. 106662306a36Sopenharmony_ci * 106762306a36Sopenharmony_ci * A small bit of code is duplicated here and below, to avoid error 106862306a36Sopenharmony_ci * skb allocation when it will just be freed immediately. 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci if (isrc == CEVTIND_BEI) { 107162306a36Sopenharmony_ci int ret; 107262306a36Sopenharmony_ci netdev_dbg(mod->ndev, "bus error interrupt\n"); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* TX error */ 107562306a36Sopenharmony_ci if (!(ecc & ECC_DIR)) { 107662306a36Sopenharmony_ci kfree_skb(skb_dequeue(&mod->echoq)); 107762306a36Sopenharmony_ci stats->tx_errors++; 107862306a36Sopenharmony_ci } else { 107962306a36Sopenharmony_ci stats->rx_errors++; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* 108362306a36Sopenharmony_ci * The controller automatically disables bus-error interrupts 108462306a36Sopenharmony_ci * and therefore we must re-enable them. 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ci ret = ican3_set_buserror(mod, 1); 108762306a36Sopenharmony_ci if (ret) { 108862306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to re-enable bus-error\n"); 108962306a36Sopenharmony_ci return ret; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* bus error reporting is off, return immediately */ 109362306a36Sopenharmony_ci if (!(mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) 109462306a36Sopenharmony_ci return 0; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci skb = alloc_can_err_skb(dev, &cf); 109862306a36Sopenharmony_ci if (skb == NULL) 109962306a36Sopenharmony_ci return -ENOMEM; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* data overrun interrupt */ 110262306a36Sopenharmony_ci if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) { 110362306a36Sopenharmony_ci netdev_dbg(mod->ndev, "data overrun interrupt\n"); 110462306a36Sopenharmony_ci cf->can_id |= CAN_ERR_CRTL; 110562306a36Sopenharmony_ci cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; 110662306a36Sopenharmony_ci stats->rx_over_errors++; 110762306a36Sopenharmony_ci stats->rx_errors++; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci /* error warning + passive interrupt */ 111162306a36Sopenharmony_ci if (isrc == CEVTIND_EI) { 111262306a36Sopenharmony_ci netdev_dbg(mod->ndev, "error warning + passive interrupt\n"); 111362306a36Sopenharmony_ci if (status & SR_BS) { 111462306a36Sopenharmony_ci state = CAN_STATE_BUS_OFF; 111562306a36Sopenharmony_ci cf->can_id |= CAN_ERR_BUSOFF; 111662306a36Sopenharmony_ci mod->can.can_stats.bus_off++; 111762306a36Sopenharmony_ci can_bus_off(dev); 111862306a36Sopenharmony_ci } else if (status & SR_ES) { 111962306a36Sopenharmony_ci if (rxerr >= 128 || txerr >= 128) 112062306a36Sopenharmony_ci state = CAN_STATE_ERROR_PASSIVE; 112162306a36Sopenharmony_ci else 112262306a36Sopenharmony_ci state = CAN_STATE_ERROR_WARNING; 112362306a36Sopenharmony_ci } else { 112462306a36Sopenharmony_ci state = CAN_STATE_ERROR_ACTIVE; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci /* bus error interrupt */ 112962306a36Sopenharmony_ci if (isrc == CEVTIND_BEI) { 113062306a36Sopenharmony_ci mod->can.can_stats.bus_error++; 113162306a36Sopenharmony_ci cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR | CAN_ERR_CNT; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci switch (ecc & ECC_MASK) { 113462306a36Sopenharmony_ci case ECC_BIT: 113562306a36Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_BIT; 113662306a36Sopenharmony_ci break; 113762306a36Sopenharmony_ci case ECC_FORM: 113862306a36Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_FORM; 113962306a36Sopenharmony_ci break; 114062306a36Sopenharmony_ci case ECC_STUFF: 114162306a36Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_STUFF; 114262306a36Sopenharmony_ci break; 114362306a36Sopenharmony_ci default: 114462306a36Sopenharmony_ci cf->data[3] = ecc & ECC_SEG; 114562306a36Sopenharmony_ci break; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if (!(ecc & ECC_DIR)) 114962306a36Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_TX; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci cf->data[6] = txerr; 115262306a36Sopenharmony_ci cf->data[7] = rxerr; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING || 115662306a36Sopenharmony_ci state == CAN_STATE_ERROR_PASSIVE)) { 115762306a36Sopenharmony_ci cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; 115862306a36Sopenharmony_ci if (state == CAN_STATE_ERROR_WARNING) { 115962306a36Sopenharmony_ci mod->can.can_stats.error_warning++; 116062306a36Sopenharmony_ci cf->data[1] = (txerr > rxerr) ? 116162306a36Sopenharmony_ci CAN_ERR_CRTL_TX_WARNING : 116262306a36Sopenharmony_ci CAN_ERR_CRTL_RX_WARNING; 116362306a36Sopenharmony_ci } else { 116462306a36Sopenharmony_ci mod->can.can_stats.error_passive++; 116562306a36Sopenharmony_ci cf->data[1] = (txerr > rxerr) ? 116662306a36Sopenharmony_ci CAN_ERR_CRTL_TX_PASSIVE : 116762306a36Sopenharmony_ci CAN_ERR_CRTL_RX_PASSIVE; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci cf->data[6] = txerr; 117162306a36Sopenharmony_ci cf->data[7] = rxerr; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci mod->can.state = state; 117562306a36Sopenharmony_ci netif_rx(skb); 117662306a36Sopenharmony_ci return 0; 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cistatic void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci switch (msg->data[0]) { 118262306a36Sopenharmony_ci case INQUIRY_STATUS: 118362306a36Sopenharmony_ci case INQUIRY_EXTENDED: 118462306a36Sopenharmony_ci mod->bec.rxerr = msg->data[5]; 118562306a36Sopenharmony_ci mod->bec.txerr = msg->data[6]; 118662306a36Sopenharmony_ci complete(&mod->buserror_comp); 118762306a36Sopenharmony_ci break; 118862306a36Sopenharmony_ci case INQUIRY_TERMINATION: 118962306a36Sopenharmony_ci mod->termination_enabled = msg->data[6] & HWCONF_TERMINATE_ON; 119062306a36Sopenharmony_ci complete(&mod->termination_comp); 119162306a36Sopenharmony_ci break; 119262306a36Sopenharmony_ci default: 119362306a36Sopenharmony_ci netdev_err(mod->ndev, "received an unknown inquiry response\n"); 119462306a36Sopenharmony_ci break; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci/* Handle NMTS Slave Event Indication Messages from the firmware */ 119962306a36Sopenharmony_cistatic void ican3_handle_nmtsind(struct ican3_dev *mod, struct ican3_msg *msg) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci u16 subspec; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci subspec = msg->data[0] + msg->data[1] * 0x100; 120462306a36Sopenharmony_ci if (subspec == NMTS_SLAVE_EVENT_IND) { 120562306a36Sopenharmony_ci switch (msg->data[2]) { 120662306a36Sopenharmony_ci case NE_LOCAL_OCCURRED: 120762306a36Sopenharmony_ci case NE_LOCAL_RESOLVED: 120862306a36Sopenharmony_ci /* now follows the same message as Raw ICANOS CEVTIND 120962306a36Sopenharmony_ci * shift the data at the same place and call this method 121062306a36Sopenharmony_ci */ 121162306a36Sopenharmony_ci le16_add_cpu(&msg->len, -3); 121262306a36Sopenharmony_ci memmove(msg->data, msg->data + 3, le16_to_cpu(msg->len)); 121362306a36Sopenharmony_ci ican3_handle_cevtind(mod, msg); 121462306a36Sopenharmony_ci break; 121562306a36Sopenharmony_ci case NE_REMOTE_OCCURRED: 121662306a36Sopenharmony_ci case NE_REMOTE_RESOLVED: 121762306a36Sopenharmony_ci /* should not occurre, ignore */ 121862306a36Sopenharmony_ci break; 121962306a36Sopenharmony_ci default: 122062306a36Sopenharmony_ci netdev_warn(mod->ndev, "unknown NMTS event indication %x\n", 122162306a36Sopenharmony_ci msg->data[2]); 122262306a36Sopenharmony_ci break; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci } else if (subspec == NMTS_SLAVE_STATE_IND) { 122562306a36Sopenharmony_ci /* ignore state indications */ 122662306a36Sopenharmony_ci } else { 122762306a36Sopenharmony_ci netdev_warn(mod->ndev, "unhandled NMTS indication %x\n", 122862306a36Sopenharmony_ci subspec); 122962306a36Sopenharmony_ci return; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci} 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cistatic void ican3_handle_unknown_message(struct ican3_dev *mod, 123462306a36Sopenharmony_ci struct ican3_msg *msg) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci netdev_warn(mod->ndev, "received unknown message: spec 0x%.2x length %d\n", 123762306a36Sopenharmony_ci msg->spec, le16_to_cpu(msg->len)); 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci/* 124162306a36Sopenharmony_ci * Handle a control message from the firmware 124262306a36Sopenharmony_ci */ 124362306a36Sopenharmony_cistatic void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci netdev_dbg(mod->ndev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__, 124662306a36Sopenharmony_ci mod->num, msg->spec, le16_to_cpu(msg->len)); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci switch (msg->spec) { 124962306a36Sopenharmony_ci case MSG_IDVERS: 125062306a36Sopenharmony_ci ican3_handle_idvers(mod, msg); 125162306a36Sopenharmony_ci break; 125262306a36Sopenharmony_ci case MSG_MSGLOST: 125362306a36Sopenharmony_ci case MSG_FMSGLOST: 125462306a36Sopenharmony_ci ican3_handle_msglost(mod, msg); 125562306a36Sopenharmony_ci break; 125662306a36Sopenharmony_ci case MSG_CEVTIND: 125762306a36Sopenharmony_ci ican3_handle_cevtind(mod, msg); 125862306a36Sopenharmony_ci break; 125962306a36Sopenharmony_ci case MSG_INQUIRY: 126062306a36Sopenharmony_ci ican3_handle_inquiry(mod, msg); 126162306a36Sopenharmony_ci break; 126262306a36Sopenharmony_ci case MSG_NMTS: 126362306a36Sopenharmony_ci ican3_handle_nmtsind(mod, msg); 126462306a36Sopenharmony_ci break; 126562306a36Sopenharmony_ci default: 126662306a36Sopenharmony_ci ican3_handle_unknown_message(mod, msg); 126762306a36Sopenharmony_ci break; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci/* 127262306a36Sopenharmony_ci * The ican3 needs to store all echo skbs, and therefore cannot 127362306a36Sopenharmony_ci * use the generic infrastructure for this. 127462306a36Sopenharmony_ci */ 127562306a36Sopenharmony_cistatic void ican3_put_echo_skb(struct ican3_dev *mod, struct sk_buff *skb) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci skb = can_create_echo_skb(skb); 127862306a36Sopenharmony_ci if (!skb) 127962306a36Sopenharmony_ci return; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci skb_tx_timestamp(skb); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci /* save this skb for tx interrupt echo handling */ 128462306a36Sopenharmony_ci skb_queue_tail(&mod->echoq, skb); 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic unsigned int ican3_get_echo_skb(struct ican3_dev *mod) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci struct sk_buff *skb = skb_dequeue(&mod->echoq); 129062306a36Sopenharmony_ci struct can_frame *cf; 129162306a36Sopenharmony_ci u8 dlc = 0; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci /* this should never trigger unless there is a driver bug */ 129462306a36Sopenharmony_ci if (!skb) { 129562306a36Sopenharmony_ci netdev_err(mod->ndev, "BUG: echo skb not occupied\n"); 129662306a36Sopenharmony_ci return 0; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci cf = (struct can_frame *)skb->data; 130062306a36Sopenharmony_ci if (!(cf->can_id & CAN_RTR_FLAG)) 130162306a36Sopenharmony_ci dlc = cf->len; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci /* check flag whether this packet has to be looped back */ 130462306a36Sopenharmony_ci if (skb->pkt_type != PACKET_LOOPBACK) { 130562306a36Sopenharmony_ci kfree_skb(skb); 130662306a36Sopenharmony_ci return dlc; 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci skb->protocol = htons(ETH_P_CAN); 131062306a36Sopenharmony_ci skb->pkt_type = PACKET_BROADCAST; 131162306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 131262306a36Sopenharmony_ci skb->dev = mod->ndev; 131362306a36Sopenharmony_ci netif_receive_skb(skb); 131462306a36Sopenharmony_ci return dlc; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci/* 131862306a36Sopenharmony_ci * Compare an skb with an existing echo skb 131962306a36Sopenharmony_ci * 132062306a36Sopenharmony_ci * This function will be used on devices which have a hardware loopback. 132162306a36Sopenharmony_ci * On these devices, this function can be used to compare a received skb 132262306a36Sopenharmony_ci * with the saved echo skbs so that the hardware echo skb can be dropped. 132362306a36Sopenharmony_ci * 132462306a36Sopenharmony_ci * Returns true if the skb's are identical, false otherwise. 132562306a36Sopenharmony_ci */ 132662306a36Sopenharmony_cistatic bool ican3_echo_skb_matches(struct ican3_dev *mod, struct sk_buff *skb) 132762306a36Sopenharmony_ci{ 132862306a36Sopenharmony_ci struct can_frame *cf = (struct can_frame *)skb->data; 132962306a36Sopenharmony_ci struct sk_buff *echo_skb = skb_peek(&mod->echoq); 133062306a36Sopenharmony_ci struct can_frame *echo_cf; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (!echo_skb) 133362306a36Sopenharmony_ci return false; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci echo_cf = (struct can_frame *)echo_skb->data; 133662306a36Sopenharmony_ci if (cf->can_id != echo_cf->can_id) 133762306a36Sopenharmony_ci return false; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (cf->len != echo_cf->len) 134062306a36Sopenharmony_ci return false; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci return memcmp(cf->data, echo_cf->data, cf->len) == 0; 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci/* 134662306a36Sopenharmony_ci * Check that there is room in the TX ring to transmit another skb 134762306a36Sopenharmony_ci * 134862306a36Sopenharmony_ci * LOCKING: must hold mod->lock 134962306a36Sopenharmony_ci */ 135062306a36Sopenharmony_cistatic bool ican3_txok(struct ican3_dev *mod) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci struct ican3_fast_desc __iomem *desc; 135362306a36Sopenharmony_ci u8 control; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* check that we have echo queue space */ 135662306a36Sopenharmony_ci if (skb_queue_len(&mod->echoq) >= ICAN3_TX_BUFFERS) 135762306a36Sopenharmony_ci return false; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci /* copy the control bits of the descriptor */ 136062306a36Sopenharmony_ci ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16)); 136162306a36Sopenharmony_ci desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc)); 136262306a36Sopenharmony_ci control = ioread8(&desc->control); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* if the control bits are not valid, then we have no more space */ 136562306a36Sopenharmony_ci if (!(control & DESC_VALID)) 136662306a36Sopenharmony_ci return false; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return true; 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci/* 137262306a36Sopenharmony_ci * Receive one CAN frame from the hardware 137362306a36Sopenharmony_ci * 137462306a36Sopenharmony_ci * CONTEXT: must be called from user context 137562306a36Sopenharmony_ci */ 137662306a36Sopenharmony_cistatic int ican3_recv_skb(struct ican3_dev *mod) 137762306a36Sopenharmony_ci{ 137862306a36Sopenharmony_ci struct net_device *ndev = mod->ndev; 137962306a36Sopenharmony_ci struct net_device_stats *stats = &ndev->stats; 138062306a36Sopenharmony_ci struct ican3_fast_desc desc; 138162306a36Sopenharmony_ci void __iomem *desc_addr; 138262306a36Sopenharmony_ci struct can_frame *cf; 138362306a36Sopenharmony_ci struct sk_buff *skb; 138462306a36Sopenharmony_ci unsigned long flags; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci spin_lock_irqsave(&mod->lock, flags); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci /* copy the whole descriptor */ 138962306a36Sopenharmony_ci ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16)); 139062306a36Sopenharmony_ci desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc)); 139162306a36Sopenharmony_ci memcpy_fromio(&desc, desc_addr, sizeof(desc)); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci spin_unlock_irqrestore(&mod->lock, flags); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci /* check that we actually have a CAN frame */ 139662306a36Sopenharmony_ci if (!(desc.control & DESC_VALID)) 139762306a36Sopenharmony_ci return -ENOBUFS; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci /* allocate an skb */ 140062306a36Sopenharmony_ci skb = alloc_can_skb(ndev, &cf); 140162306a36Sopenharmony_ci if (unlikely(skb == NULL)) { 140262306a36Sopenharmony_ci stats->rx_dropped++; 140362306a36Sopenharmony_ci goto err_noalloc; 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci /* convert the ICAN3 frame into Linux CAN format */ 140762306a36Sopenharmony_ci ican3_to_can_frame(mod, &desc, cf); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci /* 141062306a36Sopenharmony_ci * If this is an ECHO frame received from the hardware loopback 141162306a36Sopenharmony_ci * feature, use the skb saved in the ECHO stack instead. This allows 141262306a36Sopenharmony_ci * the Linux CAN core to support CAN_RAW_RECV_OWN_MSGS correctly. 141362306a36Sopenharmony_ci * 141462306a36Sopenharmony_ci * Since this is a confirmation of a successfully transmitted packet 141562306a36Sopenharmony_ci * sent from this host, update the transmit statistics. 141662306a36Sopenharmony_ci * 141762306a36Sopenharmony_ci * Also, the netdevice queue needs to be allowed to send packets again. 141862306a36Sopenharmony_ci */ 141962306a36Sopenharmony_ci if (ican3_echo_skb_matches(mod, skb)) { 142062306a36Sopenharmony_ci stats->tx_packets++; 142162306a36Sopenharmony_ci stats->tx_bytes += ican3_get_echo_skb(mod); 142262306a36Sopenharmony_ci kfree_skb(skb); 142362306a36Sopenharmony_ci goto err_noalloc; 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci /* update statistics, receive the skb */ 142762306a36Sopenharmony_ci stats->rx_packets++; 142862306a36Sopenharmony_ci if (!(cf->can_id & CAN_RTR_FLAG)) 142962306a36Sopenharmony_ci stats->rx_bytes += cf->len; 143062306a36Sopenharmony_ci netif_receive_skb(skb); 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_cierr_noalloc: 143362306a36Sopenharmony_ci /* toggle the valid bit and return the descriptor to the ring */ 143462306a36Sopenharmony_ci desc.control ^= DESC_VALID; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci spin_lock_irqsave(&mod->lock, flags); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16)); 143962306a36Sopenharmony_ci memcpy_toio(desc_addr, &desc, 1); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* update the next buffer pointer */ 144262306a36Sopenharmony_ci mod->fastrx_num = (desc.control & DESC_WRAP) ? 0 144362306a36Sopenharmony_ci : (mod->fastrx_num + 1); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci /* there are still more buffers to process */ 144662306a36Sopenharmony_ci spin_unlock_irqrestore(&mod->lock, flags); 144762306a36Sopenharmony_ci return 0; 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic int ican3_napi(struct napi_struct *napi, int budget) 145162306a36Sopenharmony_ci{ 145262306a36Sopenharmony_ci struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi); 145362306a36Sopenharmony_ci unsigned long flags; 145462306a36Sopenharmony_ci int received = 0; 145562306a36Sopenharmony_ci int ret; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci /* process all communication messages */ 145862306a36Sopenharmony_ci while (true) { 145962306a36Sopenharmony_ci struct ican3_msg msg; 146062306a36Sopenharmony_ci ret = ican3_recv_msg(mod, &msg); 146162306a36Sopenharmony_ci if (ret) 146262306a36Sopenharmony_ci break; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci ican3_handle_message(mod, &msg); 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci /* process all CAN frames from the fast interface */ 146862306a36Sopenharmony_ci while (received < budget) { 146962306a36Sopenharmony_ci ret = ican3_recv_skb(mod); 147062306a36Sopenharmony_ci if (ret) 147162306a36Sopenharmony_ci break; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci received++; 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* We have processed all packets that the adapter had, but it 147762306a36Sopenharmony_ci * was less than our budget, stop polling */ 147862306a36Sopenharmony_ci if (received < budget) 147962306a36Sopenharmony_ci napi_complete_done(napi, received); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci spin_lock_irqsave(&mod->lock, flags); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci /* Wake up the transmit queue if necessary */ 148462306a36Sopenharmony_ci if (netif_queue_stopped(mod->ndev) && ican3_txok(mod)) 148562306a36Sopenharmony_ci netif_wake_queue(mod->ndev); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci spin_unlock_irqrestore(&mod->lock, flags); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci /* re-enable interrupt generation */ 149062306a36Sopenharmony_ci iowrite8(1 << mod->num, &mod->ctrl->int_enable); 149162306a36Sopenharmony_ci return received; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic irqreturn_t ican3_irq(int irq, void *dev_id) 149562306a36Sopenharmony_ci{ 149662306a36Sopenharmony_ci struct ican3_dev *mod = dev_id; 149762306a36Sopenharmony_ci u8 stat; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci /* 150062306a36Sopenharmony_ci * The interrupt status register on this device reports interrupts 150162306a36Sopenharmony_ci * as zeroes instead of using ones like most other devices 150262306a36Sopenharmony_ci */ 150362306a36Sopenharmony_ci stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num); 150462306a36Sopenharmony_ci if (stat == (1 << mod->num)) 150562306a36Sopenharmony_ci return IRQ_NONE; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci /* clear the MODULbus interrupt from the microcontroller */ 150862306a36Sopenharmony_ci ioread8(&mod->dpmctrl->interrupt); 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci /* disable interrupt generation, schedule the NAPI poller */ 151162306a36Sopenharmony_ci iowrite8(1 << mod->num, &mod->ctrl->int_disable); 151262306a36Sopenharmony_ci napi_schedule(&mod->napi); 151362306a36Sopenharmony_ci return IRQ_HANDLED; 151462306a36Sopenharmony_ci} 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci/* 151762306a36Sopenharmony_ci * Firmware reset, startup, and shutdown 151862306a36Sopenharmony_ci */ 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci/* 152162306a36Sopenharmony_ci * Reset an ICAN module to its power-on state 152262306a36Sopenharmony_ci * 152362306a36Sopenharmony_ci * CONTEXT: no network device registered 152462306a36Sopenharmony_ci */ 152562306a36Sopenharmony_cistatic int ican3_reset_module(struct ican3_dev *mod) 152662306a36Sopenharmony_ci{ 152762306a36Sopenharmony_ci unsigned long start; 152862306a36Sopenharmony_ci u8 runold, runnew; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci /* disable interrupts so no more work is scheduled */ 153162306a36Sopenharmony_ci iowrite8(1 << mod->num, &mod->ctrl->int_disable); 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci /* the first unallocated page in the DPM is #9 */ 153462306a36Sopenharmony_ci mod->free_page = DPM_FREE_START; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_OLD_CONTROL); 153762306a36Sopenharmony_ci runold = ioread8(mod->dpm + TARGET_RUNNING); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci /* reset the module */ 154062306a36Sopenharmony_ci iowrite8(0x00, &mod->dpmctrl->hwreset); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci /* wait until the module has finished resetting and is running */ 154362306a36Sopenharmony_ci start = jiffies; 154462306a36Sopenharmony_ci do { 154562306a36Sopenharmony_ci ican3_set_page(mod, QUEUE_OLD_CONTROL); 154662306a36Sopenharmony_ci runnew = ioread8(mod->dpm + TARGET_RUNNING); 154762306a36Sopenharmony_ci if (runnew == (runold ^ 0xff)) 154862306a36Sopenharmony_ci return 0; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci msleep(10); 155162306a36Sopenharmony_ci } while (time_before(jiffies, start + HZ / 2)); 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci netdev_err(mod->ndev, "failed to reset CAN module\n"); 155462306a36Sopenharmony_ci return -ETIMEDOUT; 155562306a36Sopenharmony_ci} 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cistatic void ican3_shutdown_module(struct ican3_dev *mod) 155862306a36Sopenharmony_ci{ 155962306a36Sopenharmony_ci ican3_msg_disconnect(mod); 156062306a36Sopenharmony_ci ican3_reset_module(mod); 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci/* 156462306a36Sopenharmony_ci * Startup an ICAN module, bringing it into fast mode 156562306a36Sopenharmony_ci */ 156662306a36Sopenharmony_cistatic int ican3_startup_module(struct ican3_dev *mod) 156762306a36Sopenharmony_ci{ 156862306a36Sopenharmony_ci int ret; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci ret = ican3_reset_module(mod); 157162306a36Sopenharmony_ci if (ret) { 157262306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to reset module\n"); 157362306a36Sopenharmony_ci return ret; 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci /* detect firmware */ 157762306a36Sopenharmony_ci memcpy_fromio(mod->fwinfo, mod->dpm + FIRMWARE_STAMP, sizeof(mod->fwinfo) - 1); 157862306a36Sopenharmony_ci if (strncmp(mod->fwinfo, "JANZ-ICAN3", 10)) { 157962306a36Sopenharmony_ci netdev_err(mod->ndev, "ICAN3 not detected (found %s)\n", mod->fwinfo); 158062306a36Sopenharmony_ci return -ENODEV; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci if (strstr(mod->fwinfo, "CAL/CANopen")) 158362306a36Sopenharmony_ci mod->fwtype = ICAN3_FWTYPE_CAL_CANOPEN; 158462306a36Sopenharmony_ci else 158562306a36Sopenharmony_ci mod->fwtype = ICAN3_FWTYPE_ICANOS; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci /* re-enable interrupts so we can send messages */ 158862306a36Sopenharmony_ci iowrite8(1 << mod->num, &mod->ctrl->int_enable); 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci ret = ican3_msg_connect(mod); 159162306a36Sopenharmony_ci if (ret) { 159262306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to connect to module\n"); 159362306a36Sopenharmony_ci return ret; 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci ican3_init_new_host_interface(mod); 159762306a36Sopenharmony_ci ret = ican3_msg_newhostif(mod); 159862306a36Sopenharmony_ci if (ret) { 159962306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to switch to new-style interface\n"); 160062306a36Sopenharmony_ci return ret; 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci /* default to "termination on" */ 160462306a36Sopenharmony_ci ret = ican3_set_termination(mod, true); 160562306a36Sopenharmony_ci if (ret) { 160662306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to enable termination\n"); 160762306a36Sopenharmony_ci return ret; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci /* default to "bus errors enabled" */ 161162306a36Sopenharmony_ci ret = ican3_set_buserror(mod, 1); 161262306a36Sopenharmony_ci if (ret) { 161362306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to set bus-error\n"); 161462306a36Sopenharmony_ci return ret; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci ican3_init_fast_host_interface(mod); 161862306a36Sopenharmony_ci ret = ican3_msg_fasthostif(mod); 161962306a36Sopenharmony_ci if (ret) { 162062306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to switch to fast host interface\n"); 162162306a36Sopenharmony_ci return ret; 162262306a36Sopenharmony_ci } 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci ret = ican3_set_id_filter(mod, true); 162562306a36Sopenharmony_ci if (ret) { 162662306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to set acceptance filter\n"); 162762306a36Sopenharmony_ci return ret; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci return 0; 163162306a36Sopenharmony_ci} 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci/* 163462306a36Sopenharmony_ci * CAN Network Device 163562306a36Sopenharmony_ci */ 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_cistatic int ican3_open(struct net_device *ndev) 163862306a36Sopenharmony_ci{ 163962306a36Sopenharmony_ci struct ican3_dev *mod = netdev_priv(ndev); 164062306a36Sopenharmony_ci int ret; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci /* open the CAN layer */ 164362306a36Sopenharmony_ci ret = open_candev(ndev); 164462306a36Sopenharmony_ci if (ret) { 164562306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to start CAN layer\n"); 164662306a36Sopenharmony_ci return ret; 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci /* bring the bus online */ 165062306a36Sopenharmony_ci ret = ican3_set_bus_state(mod, true); 165162306a36Sopenharmony_ci if (ret) { 165262306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to set bus-on\n"); 165362306a36Sopenharmony_ci close_candev(ndev); 165462306a36Sopenharmony_ci return ret; 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci /* start up the network device */ 165862306a36Sopenharmony_ci mod->can.state = CAN_STATE_ERROR_ACTIVE; 165962306a36Sopenharmony_ci netif_start_queue(ndev); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci return 0; 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_cistatic int ican3_stop(struct net_device *ndev) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci struct ican3_dev *mod = netdev_priv(ndev); 166762306a36Sopenharmony_ci int ret; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci /* stop the network device xmit routine */ 167062306a36Sopenharmony_ci netif_stop_queue(ndev); 167162306a36Sopenharmony_ci mod->can.state = CAN_STATE_STOPPED; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci /* bring the bus offline, stop receiving packets */ 167462306a36Sopenharmony_ci ret = ican3_set_bus_state(mod, false); 167562306a36Sopenharmony_ci if (ret) { 167662306a36Sopenharmony_ci netdev_err(mod->ndev, "unable to set bus-off\n"); 167762306a36Sopenharmony_ci return ret; 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci /* drop all outstanding echo skbs */ 168162306a36Sopenharmony_ci skb_queue_purge(&mod->echoq); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci /* close the CAN layer */ 168462306a36Sopenharmony_ci close_candev(ndev); 168562306a36Sopenharmony_ci return 0; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cistatic netdev_tx_t ican3_xmit(struct sk_buff *skb, struct net_device *ndev) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci struct ican3_dev *mod = netdev_priv(ndev); 169162306a36Sopenharmony_ci struct can_frame *cf = (struct can_frame *)skb->data; 169262306a36Sopenharmony_ci struct ican3_fast_desc desc; 169362306a36Sopenharmony_ci void __iomem *desc_addr; 169462306a36Sopenharmony_ci unsigned long flags; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci if (can_dev_dropped_skb(ndev, skb)) 169762306a36Sopenharmony_ci return NETDEV_TX_OK; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci spin_lock_irqsave(&mod->lock, flags); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci /* check that we can actually transmit */ 170262306a36Sopenharmony_ci if (!ican3_txok(mod)) { 170362306a36Sopenharmony_ci netdev_err(mod->ndev, "BUG: no free descriptors\n"); 170462306a36Sopenharmony_ci spin_unlock_irqrestore(&mod->lock, flags); 170562306a36Sopenharmony_ci return NETDEV_TX_BUSY; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* copy the control bits of the descriptor */ 170962306a36Sopenharmony_ci ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16)); 171062306a36Sopenharmony_ci desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc)); 171162306a36Sopenharmony_ci memset(&desc, 0, sizeof(desc)); 171262306a36Sopenharmony_ci memcpy_fromio(&desc, desc_addr, 1); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci /* convert the Linux CAN frame into ICAN3 format */ 171562306a36Sopenharmony_ci can_frame_to_ican3(mod, cf, &desc); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci /* 171862306a36Sopenharmony_ci * This hardware doesn't have TX-done notifications, so we'll try and 171962306a36Sopenharmony_ci * emulate it the best we can using ECHO skbs. Add the skb to the ECHO 172062306a36Sopenharmony_ci * stack. Upon packet reception, check if the ECHO skb and received 172162306a36Sopenharmony_ci * skb match, and use that to wake the queue. 172262306a36Sopenharmony_ci */ 172362306a36Sopenharmony_ci ican3_put_echo_skb(mod, skb); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci /* 172662306a36Sopenharmony_ci * the programming manual says that you must set the IVALID bit, then 172762306a36Sopenharmony_ci * interrupt, then set the valid bit. Quite weird, but it seems to be 172862306a36Sopenharmony_ci * required for this to work 172962306a36Sopenharmony_ci */ 173062306a36Sopenharmony_ci desc.control |= DESC_IVALID; 173162306a36Sopenharmony_ci memcpy_toio(desc_addr, &desc, sizeof(desc)); 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci /* generate a MODULbus interrupt to the microcontroller */ 173462306a36Sopenharmony_ci iowrite8(0x01, &mod->dpmctrl->interrupt); 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci desc.control ^= DESC_VALID; 173762306a36Sopenharmony_ci memcpy_toio(desc_addr, &desc, sizeof(desc)); 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci /* update the next buffer pointer */ 174062306a36Sopenharmony_ci mod->fasttx_num = (desc.control & DESC_WRAP) ? 0 174162306a36Sopenharmony_ci : (mod->fasttx_num + 1); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci /* if there is no free descriptor space, stop the transmit queue */ 174462306a36Sopenharmony_ci if (!ican3_txok(mod)) 174562306a36Sopenharmony_ci netif_stop_queue(ndev); 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci spin_unlock_irqrestore(&mod->lock, flags); 174862306a36Sopenharmony_ci return NETDEV_TX_OK; 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic const struct net_device_ops ican3_netdev_ops = { 175262306a36Sopenharmony_ci .ndo_open = ican3_open, 175362306a36Sopenharmony_ci .ndo_stop = ican3_stop, 175462306a36Sopenharmony_ci .ndo_start_xmit = ican3_xmit, 175562306a36Sopenharmony_ci .ndo_change_mtu = can_change_mtu, 175662306a36Sopenharmony_ci}; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_cistatic const struct ethtool_ops ican3_ethtool_ops = { 175962306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 176062306a36Sopenharmony_ci}; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci/* 176362306a36Sopenharmony_ci * Low-level CAN Device 176462306a36Sopenharmony_ci */ 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */ 176762306a36Sopenharmony_cistatic const struct can_bittiming_const ican3_bittiming_const = { 176862306a36Sopenharmony_ci .name = DRV_NAME, 176962306a36Sopenharmony_ci .tseg1_min = 1, 177062306a36Sopenharmony_ci .tseg1_max = 16, 177162306a36Sopenharmony_ci .tseg2_min = 1, 177262306a36Sopenharmony_ci .tseg2_max = 8, 177362306a36Sopenharmony_ci .sjw_max = 4, 177462306a36Sopenharmony_ci .brp_min = 1, 177562306a36Sopenharmony_ci .brp_max = 64, 177662306a36Sopenharmony_ci .brp_inc = 1, 177762306a36Sopenharmony_ci}; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_cistatic int ican3_set_mode(struct net_device *ndev, enum can_mode mode) 178062306a36Sopenharmony_ci{ 178162306a36Sopenharmony_ci struct ican3_dev *mod = netdev_priv(ndev); 178262306a36Sopenharmony_ci int ret; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci if (mode != CAN_MODE_START) 178562306a36Sopenharmony_ci return -ENOTSUPP; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci /* bring the bus online */ 178862306a36Sopenharmony_ci ret = ican3_set_bus_state(mod, true); 178962306a36Sopenharmony_ci if (ret) { 179062306a36Sopenharmony_ci netdev_err(ndev, "unable to set bus-on\n"); 179162306a36Sopenharmony_ci return ret; 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci /* start up the network device */ 179562306a36Sopenharmony_ci mod->can.state = CAN_STATE_ERROR_ACTIVE; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (netif_queue_stopped(ndev)) 179862306a36Sopenharmony_ci netif_wake_queue(ndev); 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci return 0; 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic int ican3_get_berr_counter(const struct net_device *ndev, 180462306a36Sopenharmony_ci struct can_berr_counter *bec) 180562306a36Sopenharmony_ci{ 180662306a36Sopenharmony_ci struct ican3_dev *mod = netdev_priv(ndev); 180762306a36Sopenharmony_ci int ret; 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci ret = ican3_send_inquiry(mod, INQUIRY_STATUS); 181062306a36Sopenharmony_ci if (ret) 181162306a36Sopenharmony_ci return ret; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci if (!wait_for_completion_timeout(&mod->buserror_comp, HZ)) { 181462306a36Sopenharmony_ci netdev_info(mod->ndev, "%s timed out\n", __func__); 181562306a36Sopenharmony_ci return -ETIMEDOUT; 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci bec->rxerr = mod->bec.rxerr; 181962306a36Sopenharmony_ci bec->txerr = mod->bec.txerr; 182062306a36Sopenharmony_ci return 0; 182162306a36Sopenharmony_ci} 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci/* 182462306a36Sopenharmony_ci * Sysfs Attributes 182562306a36Sopenharmony_ci */ 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_cistatic ssize_t termination_show(struct device *dev, 182862306a36Sopenharmony_ci struct device_attribute *attr, 182962306a36Sopenharmony_ci char *buf) 183062306a36Sopenharmony_ci{ 183162306a36Sopenharmony_ci struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); 183262306a36Sopenharmony_ci int ret; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci ret = ican3_send_inquiry(mod, INQUIRY_TERMINATION); 183562306a36Sopenharmony_ci if (ret) 183662306a36Sopenharmony_ci return ret; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci if (!wait_for_completion_timeout(&mod->termination_comp, HZ)) { 183962306a36Sopenharmony_ci netdev_info(mod->ndev, "%s timed out\n", __func__); 184062306a36Sopenharmony_ci return -ETIMEDOUT; 184162306a36Sopenharmony_ci } 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", mod->termination_enabled); 184462306a36Sopenharmony_ci} 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_cistatic ssize_t termination_store(struct device *dev, 184762306a36Sopenharmony_ci struct device_attribute *attr, 184862306a36Sopenharmony_ci const char *buf, size_t count) 184962306a36Sopenharmony_ci{ 185062306a36Sopenharmony_ci struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); 185162306a36Sopenharmony_ci unsigned long enable; 185262306a36Sopenharmony_ci int ret; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci if (kstrtoul(buf, 0, &enable)) 185562306a36Sopenharmony_ci return -EINVAL; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci ret = ican3_set_termination(mod, enable); 185862306a36Sopenharmony_ci if (ret) 185962306a36Sopenharmony_ci return ret; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci return count; 186262306a36Sopenharmony_ci} 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_cistatic ssize_t fwinfo_show(struct device *dev, 186562306a36Sopenharmony_ci struct device_attribute *attr, 186662306a36Sopenharmony_ci char *buf) 186762306a36Sopenharmony_ci{ 186862306a36Sopenharmony_ci struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%s\n", mod->fwinfo); 187162306a36Sopenharmony_ci} 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(termination); 187462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(fwinfo); 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_cistatic struct attribute *ican3_sysfs_attrs[] = { 187762306a36Sopenharmony_ci &dev_attr_termination.attr, 187862306a36Sopenharmony_ci &dev_attr_fwinfo.attr, 187962306a36Sopenharmony_ci NULL, 188062306a36Sopenharmony_ci}; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_cistatic const struct attribute_group ican3_sysfs_attr_group = { 188362306a36Sopenharmony_ci .attrs = ican3_sysfs_attrs, 188462306a36Sopenharmony_ci}; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci/* 188762306a36Sopenharmony_ci * PCI Subsystem 188862306a36Sopenharmony_ci */ 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_cistatic int ican3_probe(struct platform_device *pdev) 189162306a36Sopenharmony_ci{ 189262306a36Sopenharmony_ci struct janz_platform_data *pdata; 189362306a36Sopenharmony_ci struct net_device *ndev; 189462306a36Sopenharmony_ci struct ican3_dev *mod; 189562306a36Sopenharmony_ci struct resource *res; 189662306a36Sopenharmony_ci struct device *dev; 189762306a36Sopenharmony_ci int ret; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 190062306a36Sopenharmony_ci if (!pdata) 190162306a36Sopenharmony_ci return -ENXIO; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno); 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci /* save the struct device for printing */ 190662306a36Sopenharmony_ci dev = &pdev->dev; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci /* allocate the CAN device and private data */ 190962306a36Sopenharmony_ci ndev = alloc_candev(sizeof(*mod), 0); 191062306a36Sopenharmony_ci if (!ndev) { 191162306a36Sopenharmony_ci dev_err(dev, "unable to allocate CANdev\n"); 191262306a36Sopenharmony_ci ret = -ENOMEM; 191362306a36Sopenharmony_ci goto out_return; 191462306a36Sopenharmony_ci } 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci platform_set_drvdata(pdev, ndev); 191762306a36Sopenharmony_ci mod = netdev_priv(ndev); 191862306a36Sopenharmony_ci mod->ndev = ndev; 191962306a36Sopenharmony_ci mod->num = pdata->modno; 192062306a36Sopenharmony_ci netif_napi_add_weight(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS); 192162306a36Sopenharmony_ci skb_queue_head_init(&mod->echoq); 192262306a36Sopenharmony_ci spin_lock_init(&mod->lock); 192362306a36Sopenharmony_ci init_completion(&mod->termination_comp); 192462306a36Sopenharmony_ci init_completion(&mod->buserror_comp); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* setup device-specific sysfs attributes */ 192762306a36Sopenharmony_ci ndev->sysfs_groups[0] = &ican3_sysfs_attr_group; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci /* the first unallocated page in the DPM is 9 */ 193062306a36Sopenharmony_ci mod->free_page = DPM_FREE_START; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci ndev->netdev_ops = &ican3_netdev_ops; 193362306a36Sopenharmony_ci ndev->ethtool_ops = &ican3_ethtool_ops; 193462306a36Sopenharmony_ci ndev->flags |= IFF_ECHO; 193562306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci mod->can.clock.freq = ICAN3_CAN_CLOCK; 193862306a36Sopenharmony_ci mod->can.bittiming_const = &ican3_bittiming_const; 193962306a36Sopenharmony_ci mod->can.do_set_mode = ican3_set_mode; 194062306a36Sopenharmony_ci mod->can.do_get_berr_counter = ican3_get_berr_counter; 194162306a36Sopenharmony_ci mod->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES 194262306a36Sopenharmony_ci | CAN_CTRLMODE_BERR_REPORTING 194362306a36Sopenharmony_ci | CAN_CTRLMODE_ONE_SHOT; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci /* find our IRQ number */ 194662306a36Sopenharmony_ci mod->irq = platform_get_irq(pdev, 0); 194762306a36Sopenharmony_ci if (mod->irq < 0) { 194862306a36Sopenharmony_ci ret = -ENODEV; 194962306a36Sopenharmony_ci goto out_free_ndev; 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci ndev->irq = mod->irq; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci /* get access to the MODULbus registers for this module */ 195562306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 195662306a36Sopenharmony_ci if (!res) { 195762306a36Sopenharmony_ci dev_err(dev, "MODULbus registers not found\n"); 195862306a36Sopenharmony_ci ret = -ENODEV; 195962306a36Sopenharmony_ci goto out_free_ndev; 196062306a36Sopenharmony_ci } 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci mod->dpm = ioremap(res->start, resource_size(res)); 196362306a36Sopenharmony_ci if (!mod->dpm) { 196462306a36Sopenharmony_ci dev_err(dev, "MODULbus registers not ioremap\n"); 196562306a36Sopenharmony_ci ret = -ENOMEM; 196662306a36Sopenharmony_ci goto out_free_ndev; 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci /* get access to the control registers for this module */ 197262306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 197362306a36Sopenharmony_ci if (!res) { 197462306a36Sopenharmony_ci dev_err(dev, "CONTROL registers not found\n"); 197562306a36Sopenharmony_ci ret = -ENODEV; 197662306a36Sopenharmony_ci goto out_iounmap_dpm; 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci mod->ctrl = ioremap(res->start, resource_size(res)); 198062306a36Sopenharmony_ci if (!mod->ctrl) { 198162306a36Sopenharmony_ci dev_err(dev, "CONTROL registers not ioremap\n"); 198262306a36Sopenharmony_ci ret = -ENOMEM; 198362306a36Sopenharmony_ci goto out_iounmap_dpm; 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci /* disable our IRQ, then hookup the IRQ handler */ 198762306a36Sopenharmony_ci iowrite8(1 << mod->num, &mod->ctrl->int_disable); 198862306a36Sopenharmony_ci ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod); 198962306a36Sopenharmony_ci if (ret) { 199062306a36Sopenharmony_ci dev_err(dev, "unable to request IRQ\n"); 199162306a36Sopenharmony_ci goto out_iounmap_ctrl; 199262306a36Sopenharmony_ci } 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci /* reset and initialize the CAN controller into fast mode */ 199562306a36Sopenharmony_ci napi_enable(&mod->napi); 199662306a36Sopenharmony_ci ret = ican3_startup_module(mod); 199762306a36Sopenharmony_ci if (ret) { 199862306a36Sopenharmony_ci dev_err(dev, "%s: unable to start CANdev\n", __func__); 199962306a36Sopenharmony_ci goto out_free_irq; 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci /* register with the Linux CAN layer */ 200362306a36Sopenharmony_ci ret = register_candev(ndev); 200462306a36Sopenharmony_ci if (ret) { 200562306a36Sopenharmony_ci dev_err(dev, "%s: unable to register CANdev\n", __func__); 200662306a36Sopenharmony_ci goto out_free_irq; 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci netdev_info(mod->ndev, "module %d: registered CAN device\n", pdata->modno); 201062306a36Sopenharmony_ci return 0; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ciout_free_irq: 201362306a36Sopenharmony_ci napi_disable(&mod->napi); 201462306a36Sopenharmony_ci iowrite8(1 << mod->num, &mod->ctrl->int_disable); 201562306a36Sopenharmony_ci free_irq(mod->irq, mod); 201662306a36Sopenharmony_ciout_iounmap_ctrl: 201762306a36Sopenharmony_ci iounmap(mod->ctrl); 201862306a36Sopenharmony_ciout_iounmap_dpm: 201962306a36Sopenharmony_ci iounmap(mod->dpm); 202062306a36Sopenharmony_ciout_free_ndev: 202162306a36Sopenharmony_ci free_candev(ndev); 202262306a36Sopenharmony_ciout_return: 202362306a36Sopenharmony_ci return ret; 202462306a36Sopenharmony_ci} 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_cistatic void ican3_remove(struct platform_device *pdev) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 202962306a36Sopenharmony_ci struct ican3_dev *mod = netdev_priv(ndev); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci /* unregister the netdevice, stop interrupts */ 203262306a36Sopenharmony_ci unregister_netdev(ndev); 203362306a36Sopenharmony_ci napi_disable(&mod->napi); 203462306a36Sopenharmony_ci iowrite8(1 << mod->num, &mod->ctrl->int_disable); 203562306a36Sopenharmony_ci free_irq(mod->irq, mod); 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci /* put the module into reset */ 203862306a36Sopenharmony_ci ican3_shutdown_module(mod); 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci /* unmap all registers */ 204162306a36Sopenharmony_ci iounmap(mod->ctrl); 204262306a36Sopenharmony_ci iounmap(mod->dpm); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci free_candev(ndev); 204562306a36Sopenharmony_ci} 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_cistatic struct platform_driver ican3_driver = { 204862306a36Sopenharmony_ci .driver = { 204962306a36Sopenharmony_ci .name = DRV_NAME, 205062306a36Sopenharmony_ci }, 205162306a36Sopenharmony_ci .probe = ican3_probe, 205262306a36Sopenharmony_ci .remove_new = ican3_remove, 205362306a36Sopenharmony_ci}; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_cimodule_platform_driver(ican3_driver); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ciMODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); 205862306a36Sopenharmony_ciMODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver"); 205962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 206062306a36Sopenharmony_ciMODULE_ALIAS("platform:janz-ican3"); 2061