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