xref: /openbmc/linux/drivers/hsi/clients/ssi_protocol.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2dc7bf5d7SSebastian Reichel /*
3dc7bf5d7SSebastian Reichel  * ssi_protocol.c
4dc7bf5d7SSebastian Reichel  *
5dc7bf5d7SSebastian Reichel  * Implementation of the SSI McSAAB improved protocol.
6dc7bf5d7SSebastian Reichel  *
7dc7bf5d7SSebastian Reichel  * Copyright (C) 2010 Nokia Corporation. All rights reserved.
8dc7bf5d7SSebastian Reichel  * Copyright (C) 2013 Sebastian Reichel <sre@kernel.org>
9dc7bf5d7SSebastian Reichel  *
10dc7bf5d7SSebastian Reichel  * Contact: Carlos Chinea <carlos.chinea@nokia.com>
11dc7bf5d7SSebastian Reichel  */
12dc7bf5d7SSebastian Reichel 
13dc7bf5d7SSebastian Reichel #include <linux/atomic.h>
14dc7bf5d7SSebastian Reichel #include <linux/clk.h>
15dc7bf5d7SSebastian Reichel #include <linux/device.h>
16dc7bf5d7SSebastian Reichel #include <linux/err.h>
17dc7bf5d7SSebastian Reichel #include <linux/gpio.h>
18dc7bf5d7SSebastian Reichel #include <linux/if_ether.h>
19dc7bf5d7SSebastian Reichel #include <linux/if_arp.h>
20dc7bf5d7SSebastian Reichel #include <linux/if_phonet.h>
21dc7bf5d7SSebastian Reichel #include <linux/init.h>
22dc7bf5d7SSebastian Reichel #include <linux/irq.h>
23dc7bf5d7SSebastian Reichel #include <linux/list.h>
24dc7bf5d7SSebastian Reichel #include <linux/module.h>
25dc7bf5d7SSebastian Reichel #include <linux/netdevice.h>
26dc7bf5d7SSebastian Reichel #include <linux/notifier.h>
27dc7bf5d7SSebastian Reichel #include <linux/scatterlist.h>
28dc7bf5d7SSebastian Reichel #include <linux/skbuff.h>
29dc7bf5d7SSebastian Reichel #include <linux/slab.h>
30dc7bf5d7SSebastian Reichel #include <linux/spinlock.h>
31dc7bf5d7SSebastian Reichel #include <linux/timer.h>
32dc7bf5d7SSebastian Reichel #include <linux/hsi/hsi.h>
33dc7bf5d7SSebastian Reichel #include <linux/hsi/ssi_protocol.h>
34dc7bf5d7SSebastian Reichel 
35dc7bf5d7SSebastian Reichel #define SSIP_TXQUEUE_LEN	100
36dc7bf5d7SSebastian Reichel #define SSIP_MAX_MTU		65535
37dc7bf5d7SSebastian Reichel #define SSIP_DEFAULT_MTU	4000
38dc7bf5d7SSebastian Reichel #define PN_MEDIA_SOS		21
39dc7bf5d7SSebastian Reichel #define SSIP_MIN_PN_HDR		6	/* FIXME: Revisit */
40dc7bf5d7SSebastian Reichel #define SSIP_WDTOUT		2000	/* FIXME: has to be 500 msecs */
41dc7bf5d7SSebastian Reichel #define SSIP_KATOUT		15	/* 15 msecs */
42dc7bf5d7SSebastian Reichel #define SSIP_MAX_CMDS		5 /* Number of pre-allocated commands buffers */
43dc7bf5d7SSebastian Reichel #define SSIP_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1)
44dc7bf5d7SSebastian Reichel #define SSIP_CMT_LOADER_SYNC	0x11223344
45dc7bf5d7SSebastian Reichel /*
46dc7bf5d7SSebastian Reichel  * SSI protocol command definitions
47dc7bf5d7SSebastian Reichel  */
48dc7bf5d7SSebastian Reichel #define SSIP_COMMAND(data)	((data) >> 28)
49dc7bf5d7SSebastian Reichel #define SSIP_PAYLOAD(data)	((data) & 0xfffffff)
50dc7bf5d7SSebastian Reichel /* Commands */
51dc7bf5d7SSebastian Reichel #define SSIP_SW_BREAK		0
52dc7bf5d7SSebastian Reichel #define SSIP_BOOTINFO_REQ	1
53dc7bf5d7SSebastian Reichel #define SSIP_BOOTINFO_RESP	2
54dc7bf5d7SSebastian Reichel #define SSIP_WAKETEST_RESULT	3
55dc7bf5d7SSebastian Reichel #define SSIP_START_TRANS	4
56dc7bf5d7SSebastian Reichel #define SSIP_READY		5
57dc7bf5d7SSebastian Reichel /* Payloads */
58dc7bf5d7SSebastian Reichel #define SSIP_DATA_VERSION(data)	((data) & 0xff)
59dc7bf5d7SSebastian Reichel #define SSIP_LOCAL_VERID	1
60dc7bf5d7SSebastian Reichel #define SSIP_WAKETEST_OK	0
61dc7bf5d7SSebastian Reichel #define SSIP_WAKETEST_FAILED	1
62dc7bf5d7SSebastian Reichel #define SSIP_PDU_LENGTH(data)	(((data) >> 8) & 0xffff)
63dc7bf5d7SSebastian Reichel #define SSIP_MSG_ID(data)	((data) & 0xff)
64dc7bf5d7SSebastian Reichel /* Generic Command */
65dc7bf5d7SSebastian Reichel #define SSIP_CMD(cmd, payload)	(((cmd) << 28) | ((payload) & 0xfffffff))
66dc7bf5d7SSebastian Reichel /* Commands for the control channel */
67dc7bf5d7SSebastian Reichel #define SSIP_BOOTINFO_REQ_CMD(ver) \
68dc7bf5d7SSebastian Reichel 		SSIP_CMD(SSIP_BOOTINFO_REQ, SSIP_DATA_VERSION(ver))
69dc7bf5d7SSebastian Reichel #define SSIP_BOOTINFO_RESP_CMD(ver) \
70dc7bf5d7SSebastian Reichel 		SSIP_CMD(SSIP_BOOTINFO_RESP, SSIP_DATA_VERSION(ver))
71dc7bf5d7SSebastian Reichel #define SSIP_START_TRANS_CMD(pdulen, id) \
72dc7bf5d7SSebastian Reichel 		SSIP_CMD(SSIP_START_TRANS, (((pdulen) << 8) | SSIP_MSG_ID(id)))
73dc7bf5d7SSebastian Reichel #define SSIP_READY_CMD		SSIP_CMD(SSIP_READY, 0)
74dc7bf5d7SSebastian Reichel #define SSIP_SWBREAK_CMD	SSIP_CMD(SSIP_SW_BREAK, 0)
75dc7bf5d7SSebastian Reichel 
766d6c3097SSebastian Reichel #define SSIP_WAKETEST_FLAG 0
776d6c3097SSebastian Reichel 
78dc7bf5d7SSebastian Reichel /* Main state machine states */
79dc7bf5d7SSebastian Reichel enum {
80dc7bf5d7SSebastian Reichel 	INIT,
81dc7bf5d7SSebastian Reichel 	HANDSHAKE,
82dc7bf5d7SSebastian Reichel 	ACTIVE,
83dc7bf5d7SSebastian Reichel };
84dc7bf5d7SSebastian Reichel 
85dc7bf5d7SSebastian Reichel /* Send state machine states */
86dc7bf5d7SSebastian Reichel enum {
87dc7bf5d7SSebastian Reichel 	SEND_IDLE,
88dc7bf5d7SSebastian Reichel 	WAIT4READY,
89dc7bf5d7SSebastian Reichel 	SEND_READY,
90dc7bf5d7SSebastian Reichel 	SENDING,
91dc7bf5d7SSebastian Reichel 	SENDING_SWBREAK,
92dc7bf5d7SSebastian Reichel };
93dc7bf5d7SSebastian Reichel 
94dc7bf5d7SSebastian Reichel /* Receive state machine states */
95dc7bf5d7SSebastian Reichel enum {
96dc7bf5d7SSebastian Reichel 	RECV_IDLE,
97dc7bf5d7SSebastian Reichel 	RECV_READY,
98dc7bf5d7SSebastian Reichel 	RECEIVING,
99dc7bf5d7SSebastian Reichel };
100dc7bf5d7SSebastian Reichel 
101dc7bf5d7SSebastian Reichel /**
102dc7bf5d7SSebastian Reichel  * struct ssi_protocol - SSI protocol (McSAAB) data
103dc7bf5d7SSebastian Reichel  * @main_state: Main state machine
104dc7bf5d7SSebastian Reichel  * @send_state: TX state machine
105dc7bf5d7SSebastian Reichel  * @recv_state: RX state machine
1066d6c3097SSebastian Reichel  * @flags: Flags, currently only used to follow wake line test
107dc7bf5d7SSebastian Reichel  * @rxid: RX data id
108dc7bf5d7SSebastian Reichel  * @txid: TX data id
109dc7bf5d7SSebastian Reichel  * @txqueue_len: TX queue length
110dc7bf5d7SSebastian Reichel  * @tx_wd: TX watchdog
111dc7bf5d7SSebastian Reichel  * @rx_wd: RX watchdog
112dc7bf5d7SSebastian Reichel  * @keep_alive: Workaround for SSI HW bug
113dc7bf5d7SSebastian Reichel  * @lock: To serialize access to this struct
114dc7bf5d7SSebastian Reichel  * @netdev: Phonet network device
115dc7bf5d7SSebastian Reichel  * @txqueue: TX data queue
116dc7bf5d7SSebastian Reichel  * @cmdqueue: Queue of free commands
117dc7bf5d7SSebastian Reichel  * @cl: HSI client own reference
118dc7bf5d7SSebastian Reichel  * @link: Link for ssip_list
119dc7bf5d7SSebastian Reichel  * @tx_usecount: Refcount to keep track the slaves that use the wake line
120dc7bf5d7SSebastian Reichel  * @channel_id_cmd: HSI channel id for command stream
121dc7bf5d7SSebastian Reichel  * @channel_id_data: HSI channel id for data stream
122dc7bf5d7SSebastian Reichel  */
123dc7bf5d7SSebastian Reichel struct ssi_protocol {
124dc7bf5d7SSebastian Reichel 	unsigned int		main_state;
125dc7bf5d7SSebastian Reichel 	unsigned int		send_state;
126dc7bf5d7SSebastian Reichel 	unsigned int		recv_state;
1276d6c3097SSebastian Reichel 	unsigned long		flags;
128dc7bf5d7SSebastian Reichel 	u8			rxid;
129dc7bf5d7SSebastian Reichel 	u8			txid;
130dc7bf5d7SSebastian Reichel 	unsigned int		txqueue_len;
131dc7bf5d7SSebastian Reichel 	struct timer_list	tx_wd;
132dc7bf5d7SSebastian Reichel 	struct timer_list	rx_wd;
133dc7bf5d7SSebastian Reichel 	struct timer_list	keep_alive; /* wake-up workaround */
134dc7bf5d7SSebastian Reichel 	spinlock_t		lock;
135dc7bf5d7SSebastian Reichel 	struct net_device	*netdev;
136dc7bf5d7SSebastian Reichel 	struct list_head	txqueue;
137dc7bf5d7SSebastian Reichel 	struct list_head	cmdqueue;
138df26d639SSebastian Reichel 	struct work_struct	work;
139dc7bf5d7SSebastian Reichel 	struct hsi_client	*cl;
140dc7bf5d7SSebastian Reichel 	struct list_head	link;
141dc7bf5d7SSebastian Reichel 	atomic_t		tx_usecnt;
142dc7bf5d7SSebastian Reichel 	int			channel_id_cmd;
143dc7bf5d7SSebastian Reichel 	int			channel_id_data;
144dc7bf5d7SSebastian Reichel };
145dc7bf5d7SSebastian Reichel 
146dc7bf5d7SSebastian Reichel /* List of ssi protocol instances */
147dc7bf5d7SSebastian Reichel static LIST_HEAD(ssip_list);
148dc7bf5d7SSebastian Reichel 
149dc7bf5d7SSebastian Reichel static void ssip_rxcmd_complete(struct hsi_msg *msg);
150dc7bf5d7SSebastian Reichel 
ssip_set_cmd(struct hsi_msg * msg,u32 cmd)151dc7bf5d7SSebastian Reichel static inline void ssip_set_cmd(struct hsi_msg *msg, u32 cmd)
152dc7bf5d7SSebastian Reichel {
153dc7bf5d7SSebastian Reichel 	u32 *data;
154dc7bf5d7SSebastian Reichel 
155dc7bf5d7SSebastian Reichel 	data = sg_virt(msg->sgt.sgl);
156dc7bf5d7SSebastian Reichel 	*data = cmd;
157dc7bf5d7SSebastian Reichel }
158dc7bf5d7SSebastian Reichel 
ssip_get_cmd(struct hsi_msg * msg)159dc7bf5d7SSebastian Reichel static inline u32 ssip_get_cmd(struct hsi_msg *msg)
160dc7bf5d7SSebastian Reichel {
161dc7bf5d7SSebastian Reichel 	u32 *data;
162dc7bf5d7SSebastian Reichel 
163dc7bf5d7SSebastian Reichel 	data = sg_virt(msg->sgt.sgl);
164dc7bf5d7SSebastian Reichel 
165dc7bf5d7SSebastian Reichel 	return *data;
166dc7bf5d7SSebastian Reichel }
167dc7bf5d7SSebastian Reichel 
ssip_skb_to_msg(struct sk_buff * skb,struct hsi_msg * msg)168dc7bf5d7SSebastian Reichel static void ssip_skb_to_msg(struct sk_buff *skb, struct hsi_msg *msg)
169dc7bf5d7SSebastian Reichel {
170dc7bf5d7SSebastian Reichel 	skb_frag_t *frag;
171dc7bf5d7SSebastian Reichel 	struct scatterlist *sg;
172dc7bf5d7SSebastian Reichel 	int i;
173dc7bf5d7SSebastian Reichel 
174dc7bf5d7SSebastian Reichel 	BUG_ON(msg->sgt.nents != (unsigned int)(skb_shinfo(skb)->nr_frags + 1));
175dc7bf5d7SSebastian Reichel 
176dc7bf5d7SSebastian Reichel 	sg = msg->sgt.sgl;
177dc7bf5d7SSebastian Reichel 	sg_set_buf(sg, skb->data, skb_headlen(skb));
178dc7bf5d7SSebastian Reichel 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
179dc7bf5d7SSebastian Reichel 		sg = sg_next(sg);
180dc7bf5d7SSebastian Reichel 		BUG_ON(!sg);
181dc7bf5d7SSebastian Reichel 		frag = &skb_shinfo(skb)->frags[i];
182d7840976SMatthew Wilcox (Oracle) 		sg_set_page(sg, skb_frag_page(frag), skb_frag_size(frag),
183b54c9d5bSJonathan Lemon 				skb_frag_off(frag));
184dc7bf5d7SSebastian Reichel 	}
185dc7bf5d7SSebastian Reichel }
186dc7bf5d7SSebastian Reichel 
ssip_free_data(struct hsi_msg * msg)187dc7bf5d7SSebastian Reichel static void ssip_free_data(struct hsi_msg *msg)
188dc7bf5d7SSebastian Reichel {
189dc7bf5d7SSebastian Reichel 	struct sk_buff *skb;
190dc7bf5d7SSebastian Reichel 
191dc7bf5d7SSebastian Reichel 	skb = msg->context;
192dc7bf5d7SSebastian Reichel 	pr_debug("free data: msg %p context %p skb %p\n", msg, msg->context,
193dc7bf5d7SSebastian Reichel 								skb);
194dc7bf5d7SSebastian Reichel 	msg->destructor = NULL;
195dc7bf5d7SSebastian Reichel 	dev_kfree_skb(skb);
196dc7bf5d7SSebastian Reichel 	hsi_free_msg(msg);
197dc7bf5d7SSebastian Reichel }
198dc7bf5d7SSebastian Reichel 
ssip_alloc_data(struct ssi_protocol * ssi,struct sk_buff * skb,gfp_t flags)199dc7bf5d7SSebastian Reichel static struct hsi_msg *ssip_alloc_data(struct ssi_protocol *ssi,
200dc7bf5d7SSebastian Reichel 					struct sk_buff *skb, gfp_t flags)
201dc7bf5d7SSebastian Reichel {
202dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
203dc7bf5d7SSebastian Reichel 
204dc7bf5d7SSebastian Reichel 	msg = hsi_alloc_msg(skb_shinfo(skb)->nr_frags + 1, flags);
205dc7bf5d7SSebastian Reichel 	if (!msg)
206dc7bf5d7SSebastian Reichel 		return NULL;
207dc7bf5d7SSebastian Reichel 	ssip_skb_to_msg(skb, msg);
208dc7bf5d7SSebastian Reichel 	msg->destructor = ssip_free_data;
209dc7bf5d7SSebastian Reichel 	msg->channel = ssi->channel_id_data;
210dc7bf5d7SSebastian Reichel 	msg->context = skb;
211dc7bf5d7SSebastian Reichel 
212dc7bf5d7SSebastian Reichel 	return msg;
213dc7bf5d7SSebastian Reichel }
214dc7bf5d7SSebastian Reichel 
ssip_release_cmd(struct hsi_msg * msg)215dc7bf5d7SSebastian Reichel static inline void ssip_release_cmd(struct hsi_msg *msg)
216dc7bf5d7SSebastian Reichel {
217dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(msg->cl);
218dc7bf5d7SSebastian Reichel 
219dc7bf5d7SSebastian Reichel 	dev_dbg(&msg->cl->device, "Release cmd 0x%08x\n", ssip_get_cmd(msg));
220dc7bf5d7SSebastian Reichel 	spin_lock_bh(&ssi->lock);
221dc7bf5d7SSebastian Reichel 	list_add_tail(&msg->link, &ssi->cmdqueue);
222dc7bf5d7SSebastian Reichel 	spin_unlock_bh(&ssi->lock);
223dc7bf5d7SSebastian Reichel }
224dc7bf5d7SSebastian Reichel 
ssip_claim_cmd(struct ssi_protocol * ssi)225dc7bf5d7SSebastian Reichel static struct hsi_msg *ssip_claim_cmd(struct ssi_protocol *ssi)
226dc7bf5d7SSebastian Reichel {
227dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
228dc7bf5d7SSebastian Reichel 
229dc7bf5d7SSebastian Reichel 	BUG_ON(list_empty(&ssi->cmdqueue));
230dc7bf5d7SSebastian Reichel 
231dc7bf5d7SSebastian Reichel 	spin_lock_bh(&ssi->lock);
232dc7bf5d7SSebastian Reichel 	msg = list_first_entry(&ssi->cmdqueue, struct hsi_msg, link);
233dc7bf5d7SSebastian Reichel 	list_del(&msg->link);
234dc7bf5d7SSebastian Reichel 	spin_unlock_bh(&ssi->lock);
235dc7bf5d7SSebastian Reichel 	msg->destructor = ssip_release_cmd;
236dc7bf5d7SSebastian Reichel 
237dc7bf5d7SSebastian Reichel 	return msg;
238dc7bf5d7SSebastian Reichel }
239dc7bf5d7SSebastian Reichel 
ssip_free_cmds(struct ssi_protocol * ssi)240dc7bf5d7SSebastian Reichel static void ssip_free_cmds(struct ssi_protocol *ssi)
241dc7bf5d7SSebastian Reichel {
242dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg, *tmp;
243dc7bf5d7SSebastian Reichel 
244dc7bf5d7SSebastian Reichel 	list_for_each_entry_safe(msg, tmp, &ssi->cmdqueue, link) {
245dc7bf5d7SSebastian Reichel 		list_del(&msg->link);
246dc7bf5d7SSebastian Reichel 		msg->destructor = NULL;
247dc7bf5d7SSebastian Reichel 		kfree(sg_virt(msg->sgt.sgl));
248dc7bf5d7SSebastian Reichel 		hsi_free_msg(msg);
249dc7bf5d7SSebastian Reichel 	}
250dc7bf5d7SSebastian Reichel }
251dc7bf5d7SSebastian Reichel 
ssip_alloc_cmds(struct ssi_protocol * ssi)252dc7bf5d7SSebastian Reichel static int ssip_alloc_cmds(struct ssi_protocol *ssi)
253dc7bf5d7SSebastian Reichel {
254dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
255dc7bf5d7SSebastian Reichel 	u32 *buf;
256dc7bf5d7SSebastian Reichel 	unsigned int i;
257dc7bf5d7SSebastian Reichel 
258dc7bf5d7SSebastian Reichel 	for (i = 0; i < SSIP_MAX_CMDS; i++) {
259dc7bf5d7SSebastian Reichel 		msg = hsi_alloc_msg(1, GFP_KERNEL);
260dc7bf5d7SSebastian Reichel 		if (!msg)
261dc7bf5d7SSebastian Reichel 			goto out;
262dc7bf5d7SSebastian Reichel 		buf = kmalloc(sizeof(*buf), GFP_KERNEL);
263dc7bf5d7SSebastian Reichel 		if (!buf) {
264dc7bf5d7SSebastian Reichel 			hsi_free_msg(msg);
265dc7bf5d7SSebastian Reichel 			goto out;
266dc7bf5d7SSebastian Reichel 		}
267dc7bf5d7SSebastian Reichel 		sg_init_one(msg->sgt.sgl, buf, sizeof(*buf));
268dc7bf5d7SSebastian Reichel 		msg->channel = ssi->channel_id_cmd;
269dc7bf5d7SSebastian Reichel 		list_add_tail(&msg->link, &ssi->cmdqueue);
270dc7bf5d7SSebastian Reichel 	}
271dc7bf5d7SSebastian Reichel 
272dc7bf5d7SSebastian Reichel 	return 0;
273dc7bf5d7SSebastian Reichel out:
274dc7bf5d7SSebastian Reichel 	ssip_free_cmds(ssi);
275dc7bf5d7SSebastian Reichel 
276dc7bf5d7SSebastian Reichel 	return -ENOMEM;
277dc7bf5d7SSebastian Reichel }
278dc7bf5d7SSebastian Reichel 
ssip_set_rxstate(struct ssi_protocol * ssi,unsigned int state)279dc7bf5d7SSebastian Reichel static void ssip_set_rxstate(struct ssi_protocol *ssi, unsigned int state)
280dc7bf5d7SSebastian Reichel {
281dc7bf5d7SSebastian Reichel 	ssi->recv_state = state;
282dc7bf5d7SSebastian Reichel 	switch (state) {
283dc7bf5d7SSebastian Reichel 	case RECV_IDLE:
284dc7bf5d7SSebastian Reichel 		del_timer(&ssi->rx_wd);
285dc7bf5d7SSebastian Reichel 		if (ssi->send_state == SEND_IDLE)
286dc7bf5d7SSebastian Reichel 			del_timer(&ssi->keep_alive);
287dc7bf5d7SSebastian Reichel 		break;
288dc7bf5d7SSebastian Reichel 	case RECV_READY:
289dc7bf5d7SSebastian Reichel 		/* CMT speech workaround */
290dc7bf5d7SSebastian Reichel 		if (atomic_read(&ssi->tx_usecnt))
291dc7bf5d7SSebastian Reichel 			break;
292df561f66SGustavo A. R. Silva 		fallthrough;
293dc7bf5d7SSebastian Reichel 	case RECEIVING:
294dc7bf5d7SSebastian Reichel 		mod_timer(&ssi->keep_alive, jiffies +
295dc7bf5d7SSebastian Reichel 						msecs_to_jiffies(SSIP_KATOUT));
296dc7bf5d7SSebastian Reichel 		mod_timer(&ssi->rx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT));
297dc7bf5d7SSebastian Reichel 		break;
298dc7bf5d7SSebastian Reichel 	default:
299dc7bf5d7SSebastian Reichel 		break;
300dc7bf5d7SSebastian Reichel 	}
301dc7bf5d7SSebastian Reichel }
302dc7bf5d7SSebastian Reichel 
ssip_set_txstate(struct ssi_protocol * ssi,unsigned int state)303dc7bf5d7SSebastian Reichel static void ssip_set_txstate(struct ssi_protocol *ssi, unsigned int state)
304dc7bf5d7SSebastian Reichel {
305dc7bf5d7SSebastian Reichel 	ssi->send_state = state;
306dc7bf5d7SSebastian Reichel 	switch (state) {
307dc7bf5d7SSebastian Reichel 	case SEND_IDLE:
308dc7bf5d7SSebastian Reichel 	case SEND_READY:
309dc7bf5d7SSebastian Reichel 		del_timer(&ssi->tx_wd);
310dc7bf5d7SSebastian Reichel 		if (ssi->recv_state == RECV_IDLE)
311dc7bf5d7SSebastian Reichel 			del_timer(&ssi->keep_alive);
312dc7bf5d7SSebastian Reichel 		break;
313dc7bf5d7SSebastian Reichel 	case WAIT4READY:
314dc7bf5d7SSebastian Reichel 	case SENDING:
315dc7bf5d7SSebastian Reichel 	case SENDING_SWBREAK:
316dc7bf5d7SSebastian Reichel 		mod_timer(&ssi->keep_alive,
317dc7bf5d7SSebastian Reichel 				jiffies + msecs_to_jiffies(SSIP_KATOUT));
318dc7bf5d7SSebastian Reichel 		mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT));
319dc7bf5d7SSebastian Reichel 		break;
320dc7bf5d7SSebastian Reichel 	default:
321dc7bf5d7SSebastian Reichel 		break;
322dc7bf5d7SSebastian Reichel 	}
323dc7bf5d7SSebastian Reichel }
324dc7bf5d7SSebastian Reichel 
ssip_slave_get_master(struct hsi_client * slave)325dc7bf5d7SSebastian Reichel struct hsi_client *ssip_slave_get_master(struct hsi_client *slave)
326dc7bf5d7SSebastian Reichel {
327dc7bf5d7SSebastian Reichel 	struct hsi_client *master = ERR_PTR(-ENODEV);
328dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi;
329dc7bf5d7SSebastian Reichel 
330dc7bf5d7SSebastian Reichel 	list_for_each_entry(ssi, &ssip_list, link)
331dc7bf5d7SSebastian Reichel 		if (slave->device.parent == ssi->cl->device.parent) {
332dc7bf5d7SSebastian Reichel 			master = ssi->cl;
333dc7bf5d7SSebastian Reichel 			break;
334dc7bf5d7SSebastian Reichel 		}
335dc7bf5d7SSebastian Reichel 
336dc7bf5d7SSebastian Reichel 	return master;
337dc7bf5d7SSebastian Reichel }
338dc7bf5d7SSebastian Reichel EXPORT_SYMBOL_GPL(ssip_slave_get_master);
339dc7bf5d7SSebastian Reichel 
ssip_slave_start_tx(struct hsi_client * master)340dc7bf5d7SSebastian Reichel int ssip_slave_start_tx(struct hsi_client *master)
341dc7bf5d7SSebastian Reichel {
342dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(master);
343dc7bf5d7SSebastian Reichel 
344dc7bf5d7SSebastian Reichel 	dev_dbg(&master->device, "start TX %d\n", atomic_read(&ssi->tx_usecnt));
345dc7bf5d7SSebastian Reichel 	spin_lock_bh(&ssi->lock);
346dc7bf5d7SSebastian Reichel 	if (ssi->send_state == SEND_IDLE) {
347dc7bf5d7SSebastian Reichel 		ssip_set_txstate(ssi, WAIT4READY);
348dc7bf5d7SSebastian Reichel 		hsi_start_tx(master);
349dc7bf5d7SSebastian Reichel 	}
350dc7bf5d7SSebastian Reichel 	spin_unlock_bh(&ssi->lock);
351dc7bf5d7SSebastian Reichel 	atomic_inc(&ssi->tx_usecnt);
352dc7bf5d7SSebastian Reichel 
353dc7bf5d7SSebastian Reichel 	return 0;
354dc7bf5d7SSebastian Reichel }
355dc7bf5d7SSebastian Reichel EXPORT_SYMBOL_GPL(ssip_slave_start_tx);
356dc7bf5d7SSebastian Reichel 
ssip_slave_stop_tx(struct hsi_client * master)357dc7bf5d7SSebastian Reichel int ssip_slave_stop_tx(struct hsi_client *master)
358dc7bf5d7SSebastian Reichel {
359dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(master);
360dc7bf5d7SSebastian Reichel 
361dc7bf5d7SSebastian Reichel 	WARN_ON_ONCE(atomic_read(&ssi->tx_usecnt) == 0);
362dc7bf5d7SSebastian Reichel 
363dc7bf5d7SSebastian Reichel 	if (atomic_dec_and_test(&ssi->tx_usecnt)) {
364dc7bf5d7SSebastian Reichel 		spin_lock_bh(&ssi->lock);
365dc7bf5d7SSebastian Reichel 		if ((ssi->send_state == SEND_READY) ||
366dc7bf5d7SSebastian Reichel 			(ssi->send_state == WAIT4READY)) {
367dc7bf5d7SSebastian Reichel 			ssip_set_txstate(ssi, SEND_IDLE);
368dc7bf5d7SSebastian Reichel 			hsi_stop_tx(master);
369dc7bf5d7SSebastian Reichel 		}
370dc7bf5d7SSebastian Reichel 		spin_unlock_bh(&ssi->lock);
371dc7bf5d7SSebastian Reichel 	}
372dc7bf5d7SSebastian Reichel 	dev_dbg(&master->device, "stop TX %d\n", atomic_read(&ssi->tx_usecnt));
373dc7bf5d7SSebastian Reichel 
374dc7bf5d7SSebastian Reichel 	return 0;
375dc7bf5d7SSebastian Reichel }
376dc7bf5d7SSebastian Reichel EXPORT_SYMBOL_GPL(ssip_slave_stop_tx);
377dc7bf5d7SSebastian Reichel 
ssip_slave_running(struct hsi_client * master)378dc7bf5d7SSebastian Reichel int ssip_slave_running(struct hsi_client *master)
379dc7bf5d7SSebastian Reichel {
380dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(master);
381dc7bf5d7SSebastian Reichel 	return netif_running(ssi->netdev);
382dc7bf5d7SSebastian Reichel }
383dc7bf5d7SSebastian Reichel EXPORT_SYMBOL_GPL(ssip_slave_running);
384dc7bf5d7SSebastian Reichel 
ssip_reset(struct hsi_client * cl)385dc7bf5d7SSebastian Reichel static void ssip_reset(struct hsi_client *cl)
386dc7bf5d7SSebastian Reichel {
387dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
388dc7bf5d7SSebastian Reichel 	struct list_head *head, *tmp;
389dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
390dc7bf5d7SSebastian Reichel 
391dc7bf5d7SSebastian Reichel 	if (netif_running(ssi->netdev))
392dc7bf5d7SSebastian Reichel 		netif_carrier_off(ssi->netdev);
393dc7bf5d7SSebastian Reichel 	hsi_flush(cl);
394dc7bf5d7SSebastian Reichel 	spin_lock_bh(&ssi->lock);
395dc7bf5d7SSebastian Reichel 	if (ssi->send_state != SEND_IDLE)
396dc7bf5d7SSebastian Reichel 		hsi_stop_tx(cl);
3976d6c3097SSebastian Reichel 	spin_unlock_bh(&ssi->lock);
3986d6c3097SSebastian Reichel 	if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
3996d6c3097SSebastian Reichel 		ssi_waketest(cl, 0); /* FIXME: To be removed */
4006d6c3097SSebastian Reichel 	spin_lock_bh(&ssi->lock);
401dc7bf5d7SSebastian Reichel 	del_timer(&ssi->rx_wd);
402dc7bf5d7SSebastian Reichel 	del_timer(&ssi->tx_wd);
403dc7bf5d7SSebastian Reichel 	del_timer(&ssi->keep_alive);
404dc7bf5d7SSebastian Reichel 	ssi->main_state = 0;
405dc7bf5d7SSebastian Reichel 	ssi->send_state = 0;
406dc7bf5d7SSebastian Reichel 	ssi->recv_state = 0;
4076d6c3097SSebastian Reichel 	ssi->flags = 0;
408dc7bf5d7SSebastian Reichel 	ssi->rxid = 0;
409dc7bf5d7SSebastian Reichel 	ssi->txid = 0;
410dc7bf5d7SSebastian Reichel 	list_for_each_safe(head, tmp, &ssi->txqueue) {
411dc7bf5d7SSebastian Reichel 		msg = list_entry(head, struct hsi_msg, link);
412dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "Pending TX data\n");
413dc7bf5d7SSebastian Reichel 		list_del(head);
414dc7bf5d7SSebastian Reichel 		ssip_free_data(msg);
415dc7bf5d7SSebastian Reichel 	}
416dc7bf5d7SSebastian Reichel 	ssi->txqueue_len = 0;
417dc7bf5d7SSebastian Reichel 	spin_unlock_bh(&ssi->lock);
418dc7bf5d7SSebastian Reichel }
419dc7bf5d7SSebastian Reichel 
ssip_dump_state(struct hsi_client * cl)420dc7bf5d7SSebastian Reichel static void ssip_dump_state(struct hsi_client *cl)
421dc7bf5d7SSebastian Reichel {
422dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
423dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
424dc7bf5d7SSebastian Reichel 
425dc7bf5d7SSebastian Reichel 	spin_lock_bh(&ssi->lock);
426dc7bf5d7SSebastian Reichel 	dev_err(&cl->device, "Main state: %d\n", ssi->main_state);
427dc7bf5d7SSebastian Reichel 	dev_err(&cl->device, "Recv state: %d\n", ssi->recv_state);
428dc7bf5d7SSebastian Reichel 	dev_err(&cl->device, "Send state: %d\n", ssi->send_state);
429dc7bf5d7SSebastian Reichel 	dev_err(&cl->device, "CMT %s\n", (ssi->main_state == ACTIVE) ?
430dc7bf5d7SSebastian Reichel 							"Online" : "Offline");
4316d6c3097SSebastian Reichel 	dev_err(&cl->device, "Wake test %d\n",
4326d6c3097SSebastian Reichel 				test_bit(SSIP_WAKETEST_FLAG, &ssi->flags));
433dc7bf5d7SSebastian Reichel 	dev_err(&cl->device, "Data RX id: %d\n", ssi->rxid);
434dc7bf5d7SSebastian Reichel 	dev_err(&cl->device, "Data TX id: %d\n", ssi->txid);
435dc7bf5d7SSebastian Reichel 
436dc7bf5d7SSebastian Reichel 	list_for_each_entry(msg, &ssi->txqueue, link)
437dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "pending TX data (%p)\n", msg);
438dc7bf5d7SSebastian Reichel 	spin_unlock_bh(&ssi->lock);
439dc7bf5d7SSebastian Reichel }
440dc7bf5d7SSebastian Reichel 
ssip_error(struct hsi_client * cl)441dc7bf5d7SSebastian Reichel static void ssip_error(struct hsi_client *cl)
442dc7bf5d7SSebastian Reichel {
443dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
444dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
445dc7bf5d7SSebastian Reichel 
446dc7bf5d7SSebastian Reichel 	ssip_dump_state(cl);
447dc7bf5d7SSebastian Reichel 	ssip_reset(cl);
448dc7bf5d7SSebastian Reichel 	msg = ssip_claim_cmd(ssi);
449dc7bf5d7SSebastian Reichel 	msg->complete = ssip_rxcmd_complete;
450dc7bf5d7SSebastian Reichel 	hsi_async_read(cl, msg);
451dc7bf5d7SSebastian Reichel }
452dc7bf5d7SSebastian Reichel 
ssip_keep_alive(struct timer_list * t)453df7e828cSKees Cook static void ssip_keep_alive(struct timer_list *t)
454dc7bf5d7SSebastian Reichel {
455df7e828cSKees Cook 	struct ssi_protocol *ssi = from_timer(ssi, t, keep_alive);
456df7e828cSKees Cook 	struct hsi_client *cl = ssi->cl;
457dc7bf5d7SSebastian Reichel 
458dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "Keep alive kick in: m(%d) r(%d) s(%d)\n",
459dc7bf5d7SSebastian Reichel 		ssi->main_state, ssi->recv_state, ssi->send_state);
460dc7bf5d7SSebastian Reichel 
461dc7bf5d7SSebastian Reichel 	spin_lock(&ssi->lock);
462dc7bf5d7SSebastian Reichel 	if (ssi->recv_state == RECV_IDLE)
463dc7bf5d7SSebastian Reichel 		switch (ssi->send_state) {
464dc7bf5d7SSebastian Reichel 		case SEND_READY:
465dc7bf5d7SSebastian Reichel 			if (atomic_read(&ssi->tx_usecnt) == 0)
466dc7bf5d7SSebastian Reichel 				break;
467df561f66SGustavo A. R. Silva 			fallthrough;
468dc7bf5d7SSebastian Reichel 			/*
469e008227eSGustavo A. R. Silva 			 * Workaround for cmt-speech in that case
470e008227eSGustavo A. R. Silva 			 * we relay on audio timers.
471dc7bf5d7SSebastian Reichel 			 */
472dc7bf5d7SSebastian Reichel 		case SEND_IDLE:
473dc7bf5d7SSebastian Reichel 			spin_unlock(&ssi->lock);
474dc7bf5d7SSebastian Reichel 			return;
475dc7bf5d7SSebastian Reichel 		}
476dc7bf5d7SSebastian Reichel 	mod_timer(&ssi->keep_alive, jiffies + msecs_to_jiffies(SSIP_KATOUT));
477dc7bf5d7SSebastian Reichel 	spin_unlock(&ssi->lock);
478dc7bf5d7SSebastian Reichel }
479dc7bf5d7SSebastian Reichel 
ssip_rx_wd(struct timer_list * t)480df7e828cSKees Cook static void ssip_rx_wd(struct timer_list *t)
481dc7bf5d7SSebastian Reichel {
482df7e828cSKees Cook 	struct ssi_protocol *ssi = from_timer(ssi, t, rx_wd);
483df7e828cSKees Cook 	struct hsi_client *cl = ssi->cl;
484df7e828cSKees Cook 
485852041d1SColin Ian King 	dev_err(&cl->device, "Watchdog triggered\n");
486df7e828cSKees Cook 	ssip_error(cl);
487df7e828cSKees Cook }
488df7e828cSKees Cook 
ssip_tx_wd(struct timer_list * t)4896ac35264SKees Cook static void ssip_tx_wd(struct timer_list *t)
490df7e828cSKees Cook {
491df7e828cSKees Cook 	struct ssi_protocol *ssi = from_timer(ssi, t, tx_wd);
492df7e828cSKees Cook 	struct hsi_client *cl = ssi->cl;
493dc7bf5d7SSebastian Reichel 
494852041d1SColin Ian King 	dev_err(&cl->device, "Watchdog triggered\n");
495dc7bf5d7SSebastian Reichel 	ssip_error(cl);
496dc7bf5d7SSebastian Reichel }
497dc7bf5d7SSebastian Reichel 
ssip_send_bootinfo_req_cmd(struct hsi_client * cl)498dc7bf5d7SSebastian Reichel static void ssip_send_bootinfo_req_cmd(struct hsi_client *cl)
499dc7bf5d7SSebastian Reichel {
500dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
501dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
502dc7bf5d7SSebastian Reichel 
503dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "Issuing BOOT INFO REQ command\n");
504dc7bf5d7SSebastian Reichel 	msg = ssip_claim_cmd(ssi);
505dc7bf5d7SSebastian Reichel 	ssip_set_cmd(msg, SSIP_BOOTINFO_REQ_CMD(SSIP_LOCAL_VERID));
506dc7bf5d7SSebastian Reichel 	msg->complete = ssip_release_cmd;
507dc7bf5d7SSebastian Reichel 	hsi_async_write(cl, msg);
508dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "Issuing RX command\n");
509dc7bf5d7SSebastian Reichel 	msg = ssip_claim_cmd(ssi);
510dc7bf5d7SSebastian Reichel 	msg->complete = ssip_rxcmd_complete;
511dc7bf5d7SSebastian Reichel 	hsi_async_read(cl, msg);
512dc7bf5d7SSebastian Reichel }
513dc7bf5d7SSebastian Reichel 
ssip_start_rx(struct hsi_client * cl)514dc7bf5d7SSebastian Reichel static void ssip_start_rx(struct hsi_client *cl)
515dc7bf5d7SSebastian Reichel {
516dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
517dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
518dc7bf5d7SSebastian Reichel 
519dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "RX start M(%d) R(%d)\n", ssi->main_state,
520dc7bf5d7SSebastian Reichel 						ssi->recv_state);
521866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
522dc7bf5d7SSebastian Reichel 	/*
523dc7bf5d7SSebastian Reichel 	 * We can have two UP events in a row due to a short low
524dc7bf5d7SSebastian Reichel 	 * high transition. Therefore we need to ignore the sencond UP event.
525dc7bf5d7SSebastian Reichel 	 */
526dc7bf5d7SSebastian Reichel 	if ((ssi->main_state != ACTIVE) || (ssi->recv_state == RECV_READY)) {
527866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
528dc7bf5d7SSebastian Reichel 		return;
529dc7bf5d7SSebastian Reichel 	}
530dc7bf5d7SSebastian Reichel 	ssip_set_rxstate(ssi, RECV_READY);
531866dcb9dSSebastian Reichel 	spin_unlock_bh(&ssi->lock);
532dc7bf5d7SSebastian Reichel 
533dc7bf5d7SSebastian Reichel 	msg = ssip_claim_cmd(ssi);
534dc7bf5d7SSebastian Reichel 	ssip_set_cmd(msg, SSIP_READY_CMD);
535dc7bf5d7SSebastian Reichel 	msg->complete = ssip_release_cmd;
536dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "Send READY\n");
537dc7bf5d7SSebastian Reichel 	hsi_async_write(cl, msg);
538dc7bf5d7SSebastian Reichel }
539dc7bf5d7SSebastian Reichel 
ssip_stop_rx(struct hsi_client * cl)540dc7bf5d7SSebastian Reichel static void ssip_stop_rx(struct hsi_client *cl)
541dc7bf5d7SSebastian Reichel {
542dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
543dc7bf5d7SSebastian Reichel 
544dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "RX stop M(%d)\n", ssi->main_state);
545866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
546dc7bf5d7SSebastian Reichel 	if (likely(ssi->main_state == ACTIVE))
547dc7bf5d7SSebastian Reichel 		ssip_set_rxstate(ssi, RECV_IDLE);
548866dcb9dSSebastian Reichel 	spin_unlock_bh(&ssi->lock);
549dc7bf5d7SSebastian Reichel }
550dc7bf5d7SSebastian Reichel 
ssip_free_strans(struct hsi_msg * msg)551dc7bf5d7SSebastian Reichel static void ssip_free_strans(struct hsi_msg *msg)
552dc7bf5d7SSebastian Reichel {
553dc7bf5d7SSebastian Reichel 	ssip_free_data(msg->context);
554dc7bf5d7SSebastian Reichel 	ssip_release_cmd(msg);
555dc7bf5d7SSebastian Reichel }
556dc7bf5d7SSebastian Reichel 
ssip_strans_complete(struct hsi_msg * msg)557dc7bf5d7SSebastian Reichel static void ssip_strans_complete(struct hsi_msg *msg)
558dc7bf5d7SSebastian Reichel {
559dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = msg->cl;
560dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
561dc7bf5d7SSebastian Reichel 	struct hsi_msg *data;
562dc7bf5d7SSebastian Reichel 
563dc7bf5d7SSebastian Reichel 	data = msg->context;
564dc7bf5d7SSebastian Reichel 	ssip_release_cmd(msg);
565866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
566dc7bf5d7SSebastian Reichel 	ssip_set_txstate(ssi, SENDING);
567866dcb9dSSebastian Reichel 	spin_unlock_bh(&ssi->lock);
568dc7bf5d7SSebastian Reichel 	hsi_async_write(cl, data);
569dc7bf5d7SSebastian Reichel }
570dc7bf5d7SSebastian Reichel 
ssip_xmit(struct hsi_client * cl)571dc7bf5d7SSebastian Reichel static int ssip_xmit(struct hsi_client *cl)
572dc7bf5d7SSebastian Reichel {
573dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
574dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg, *dmsg;
575dc7bf5d7SSebastian Reichel 	struct sk_buff *skb;
576dc7bf5d7SSebastian Reichel 
577dc7bf5d7SSebastian Reichel 	spin_lock_bh(&ssi->lock);
578dc7bf5d7SSebastian Reichel 	if (list_empty(&ssi->txqueue)) {
579dc7bf5d7SSebastian Reichel 		spin_unlock_bh(&ssi->lock);
580dc7bf5d7SSebastian Reichel 		return 0;
581dc7bf5d7SSebastian Reichel 	}
582dc7bf5d7SSebastian Reichel 	dmsg = list_first_entry(&ssi->txqueue, struct hsi_msg, link);
583dc7bf5d7SSebastian Reichel 	list_del(&dmsg->link);
584dc7bf5d7SSebastian Reichel 	ssi->txqueue_len--;
585dc7bf5d7SSebastian Reichel 	spin_unlock_bh(&ssi->lock);
586dc7bf5d7SSebastian Reichel 
587dc7bf5d7SSebastian Reichel 	msg = ssip_claim_cmd(ssi);
588dc7bf5d7SSebastian Reichel 	skb = dmsg->context;
589dc7bf5d7SSebastian Reichel 	msg->context = dmsg;
590dc7bf5d7SSebastian Reichel 	msg->complete = ssip_strans_complete;
591dc7bf5d7SSebastian Reichel 	msg->destructor = ssip_free_strans;
592dc7bf5d7SSebastian Reichel 
593dc7bf5d7SSebastian Reichel 	spin_lock_bh(&ssi->lock);
594dc7bf5d7SSebastian Reichel 	ssip_set_cmd(msg, SSIP_START_TRANS_CMD(SSIP_BYTES_TO_FRAMES(skb->len),
595dc7bf5d7SSebastian Reichel 								ssi->txid));
596dc7bf5d7SSebastian Reichel 	ssi->txid++;
597dc7bf5d7SSebastian Reichel 	ssip_set_txstate(ssi, SENDING);
598dc7bf5d7SSebastian Reichel 	spin_unlock_bh(&ssi->lock);
599dc7bf5d7SSebastian Reichel 
600dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "Send STRANS (%d frames)\n",
601dc7bf5d7SSebastian Reichel 						SSIP_BYTES_TO_FRAMES(skb->len));
602dc7bf5d7SSebastian Reichel 
603dc7bf5d7SSebastian Reichel 	return hsi_async_write(cl, msg);
604dc7bf5d7SSebastian Reichel }
605dc7bf5d7SSebastian Reichel 
606dc7bf5d7SSebastian Reichel /* In soft IRQ context */
ssip_pn_rx(struct sk_buff * skb)607dc7bf5d7SSebastian Reichel static void ssip_pn_rx(struct sk_buff *skb)
608dc7bf5d7SSebastian Reichel {
609dc7bf5d7SSebastian Reichel 	struct net_device *dev = skb->dev;
610dc7bf5d7SSebastian Reichel 
611dc7bf5d7SSebastian Reichel 	if (unlikely(!netif_running(dev))) {
612dc7bf5d7SSebastian Reichel 		dev_dbg(&dev->dev, "Drop RX packet\n");
613dc7bf5d7SSebastian Reichel 		dev->stats.rx_dropped++;
614dc7bf5d7SSebastian Reichel 		dev_kfree_skb(skb);
615dc7bf5d7SSebastian Reichel 		return;
616dc7bf5d7SSebastian Reichel 	}
617dc7bf5d7SSebastian Reichel 	if (unlikely(!pskb_may_pull(skb, SSIP_MIN_PN_HDR))) {
618dc7bf5d7SSebastian Reichel 		dev_dbg(&dev->dev, "Error drop RX packet\n");
619dc7bf5d7SSebastian Reichel 		dev->stats.rx_errors++;
620dc7bf5d7SSebastian Reichel 		dev->stats.rx_length_errors++;
621dc7bf5d7SSebastian Reichel 		dev_kfree_skb(skb);
622dc7bf5d7SSebastian Reichel 		return;
623dc7bf5d7SSebastian Reichel 	}
624dc7bf5d7SSebastian Reichel 	dev->stats.rx_packets++;
625dc7bf5d7SSebastian Reichel 	dev->stats.rx_bytes += skb->len;
626dc7bf5d7SSebastian Reichel 
627dc7bf5d7SSebastian Reichel 	/* length field is exchanged in network byte order */
628dc7bf5d7SSebastian Reichel 	((u16 *)skb->data)[2] = ntohs(((u16 *)skb->data)[2]);
629dc7bf5d7SSebastian Reichel 	dev_dbg(&dev->dev, "RX length fixed (%04x -> %u)\n",
630dc7bf5d7SSebastian Reichel 			((u16 *)skb->data)[2], ntohs(((u16 *)skb->data)[2]));
631dc7bf5d7SSebastian Reichel 
632dc7bf5d7SSebastian Reichel 	skb->protocol = htons(ETH_P_PHONET);
633dc7bf5d7SSebastian Reichel 	skb_reset_mac_header(skb);
634dc7bf5d7SSebastian Reichel 	__skb_pull(skb, 1);
635dc7bf5d7SSebastian Reichel 	netif_rx(skb);
636dc7bf5d7SSebastian Reichel }
637dc7bf5d7SSebastian Reichel 
ssip_rx_data_complete(struct hsi_msg * msg)638dc7bf5d7SSebastian Reichel static void ssip_rx_data_complete(struct hsi_msg *msg)
639dc7bf5d7SSebastian Reichel {
640dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = msg->cl;
641dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
642dc7bf5d7SSebastian Reichel 	struct sk_buff *skb;
643dc7bf5d7SSebastian Reichel 
644dc7bf5d7SSebastian Reichel 	if (msg->status == HSI_STATUS_ERROR) {
645dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "RX data error\n");
646dc7bf5d7SSebastian Reichel 		ssip_free_data(msg);
647dc7bf5d7SSebastian Reichel 		ssip_error(cl);
648dc7bf5d7SSebastian Reichel 		return;
649dc7bf5d7SSebastian Reichel 	}
650dc7bf5d7SSebastian Reichel 	del_timer(&ssi->rx_wd); /* FIXME: Revisit */
651dc7bf5d7SSebastian Reichel 	skb = msg->context;
652dc7bf5d7SSebastian Reichel 	ssip_pn_rx(skb);
653dc7bf5d7SSebastian Reichel 	hsi_free_msg(msg);
654dc7bf5d7SSebastian Reichel }
655dc7bf5d7SSebastian Reichel 
ssip_rx_bootinforeq(struct hsi_client * cl,u32 cmd)656dc7bf5d7SSebastian Reichel static void ssip_rx_bootinforeq(struct hsi_client *cl, u32 cmd)
657dc7bf5d7SSebastian Reichel {
658dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
659dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
660dc7bf5d7SSebastian Reichel 
661dc7bf5d7SSebastian Reichel 	/* Workaroud: Ignore CMT Loader message leftover */
662dc7bf5d7SSebastian Reichel 	if (cmd == SSIP_CMT_LOADER_SYNC)
663dc7bf5d7SSebastian Reichel 		return;
664dc7bf5d7SSebastian Reichel 
665dc7bf5d7SSebastian Reichel 	switch (ssi->main_state) {
666dc7bf5d7SSebastian Reichel 	case ACTIVE:
667dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "Boot info req on active state\n");
668dc7bf5d7SSebastian Reichel 		ssip_error(cl);
669df561f66SGustavo A. R. Silva 		fallthrough;
670dc7bf5d7SSebastian Reichel 	case INIT:
67187d99063SSebastian Reichel 	case HANDSHAKE:
672866dcb9dSSebastian Reichel 		spin_lock_bh(&ssi->lock);
673dc7bf5d7SSebastian Reichel 		ssi->main_state = HANDSHAKE;
674866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
6756d6c3097SSebastian Reichel 
6766d6c3097SSebastian Reichel 		if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
677dc7bf5d7SSebastian Reichel 			ssi_waketest(cl, 1); /* FIXME: To be removed */
6786d6c3097SSebastian Reichel 
679866dcb9dSSebastian Reichel 		spin_lock_bh(&ssi->lock);
680dc7bf5d7SSebastian Reichel 		/* Start boot handshake watchdog */
681dc7bf5d7SSebastian Reichel 		mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT));
682866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
683dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "Send BOOTINFO_RESP\n");
684dc7bf5d7SSebastian Reichel 		if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID)
685dc7bf5d7SSebastian Reichel 			dev_warn(&cl->device, "boot info req verid mismatch\n");
686dc7bf5d7SSebastian Reichel 		msg = ssip_claim_cmd(ssi);
687dc7bf5d7SSebastian Reichel 		ssip_set_cmd(msg, SSIP_BOOTINFO_RESP_CMD(SSIP_LOCAL_VERID));
688dc7bf5d7SSebastian Reichel 		msg->complete = ssip_release_cmd;
689dc7bf5d7SSebastian Reichel 		hsi_async_write(cl, msg);
690dc7bf5d7SSebastian Reichel 		break;
691dc7bf5d7SSebastian Reichel 	default:
692dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "Wrong state M(%d)\n", ssi->main_state);
693dc7bf5d7SSebastian Reichel 		break;
694dc7bf5d7SSebastian Reichel 	}
695dc7bf5d7SSebastian Reichel }
696dc7bf5d7SSebastian Reichel 
ssip_rx_bootinforesp(struct hsi_client * cl,u32 cmd)697dc7bf5d7SSebastian Reichel static void ssip_rx_bootinforesp(struct hsi_client *cl, u32 cmd)
698dc7bf5d7SSebastian Reichel {
699dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
700dc7bf5d7SSebastian Reichel 
701dc7bf5d7SSebastian Reichel 	if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID)
702dc7bf5d7SSebastian Reichel 		dev_warn(&cl->device, "boot info resp verid mismatch\n");
703dc7bf5d7SSebastian Reichel 
704866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
705dc7bf5d7SSebastian Reichel 	if (ssi->main_state != ACTIVE)
706dc7bf5d7SSebastian Reichel 		/* Use tx_wd as a boot watchdog in non ACTIVE state */
707dc7bf5d7SSebastian Reichel 		mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT));
708dc7bf5d7SSebastian Reichel 	else
709dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "boot info resp ignored M(%d)\n",
710dc7bf5d7SSebastian Reichel 							ssi->main_state);
711866dcb9dSSebastian Reichel 	spin_unlock_bh(&ssi->lock);
712dc7bf5d7SSebastian Reichel }
713dc7bf5d7SSebastian Reichel 
ssip_rx_waketest(struct hsi_client * cl,u32 cmd)714dc7bf5d7SSebastian Reichel static void ssip_rx_waketest(struct hsi_client *cl, u32 cmd)
715dc7bf5d7SSebastian Reichel {
716dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
717dc7bf5d7SSebastian Reichel 	unsigned int wkres = SSIP_PAYLOAD(cmd);
718dc7bf5d7SSebastian Reichel 
719866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
720dc7bf5d7SSebastian Reichel 	if (ssi->main_state != HANDSHAKE) {
721dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "wake lines test ignored M(%d)\n",
722dc7bf5d7SSebastian Reichel 							ssi->main_state);
723866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
724dc7bf5d7SSebastian Reichel 		return;
725dc7bf5d7SSebastian Reichel 	}
726866dcb9dSSebastian Reichel 	spin_unlock_bh(&ssi->lock);
7276d6c3097SSebastian Reichel 
7286d6c3097SSebastian Reichel 	if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
729dc7bf5d7SSebastian Reichel 		ssi_waketest(cl, 0); /* FIXME: To be removed */
7306d6c3097SSebastian Reichel 
731866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
732dc7bf5d7SSebastian Reichel 	ssi->main_state = ACTIVE;
733dc7bf5d7SSebastian Reichel 	del_timer(&ssi->tx_wd); /* Stop boot handshake timer */
734866dcb9dSSebastian Reichel 	spin_unlock_bh(&ssi->lock);
735dc7bf5d7SSebastian Reichel 
736dc7bf5d7SSebastian Reichel 	dev_notice(&cl->device, "WAKELINES TEST %s\n",
737dc7bf5d7SSebastian Reichel 				wkres & SSIP_WAKETEST_FAILED ? "FAILED" : "OK");
738dc7bf5d7SSebastian Reichel 	if (wkres & SSIP_WAKETEST_FAILED) {
739dc7bf5d7SSebastian Reichel 		ssip_error(cl);
740dc7bf5d7SSebastian Reichel 		return;
741dc7bf5d7SSebastian Reichel 	}
742dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "CMT is ONLINE\n");
743dc7bf5d7SSebastian Reichel 	netif_wake_queue(ssi->netdev);
744dc7bf5d7SSebastian Reichel 	netif_carrier_on(ssi->netdev);
745dc7bf5d7SSebastian Reichel }
746dc7bf5d7SSebastian Reichel 
ssip_rx_ready(struct hsi_client * cl)747dc7bf5d7SSebastian Reichel static void ssip_rx_ready(struct hsi_client *cl)
748dc7bf5d7SSebastian Reichel {
749dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
750dc7bf5d7SSebastian Reichel 
751866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
752dc7bf5d7SSebastian Reichel 	if (unlikely(ssi->main_state != ACTIVE)) {
753dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "READY on wrong state: S(%d) M(%d)\n",
754dc7bf5d7SSebastian Reichel 					ssi->send_state, ssi->main_state);
755866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
756dc7bf5d7SSebastian Reichel 		return;
757dc7bf5d7SSebastian Reichel 	}
758dc7bf5d7SSebastian Reichel 	if (ssi->send_state != WAIT4READY) {
759dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "Ignore spurious READY command\n");
760866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
761dc7bf5d7SSebastian Reichel 		return;
762dc7bf5d7SSebastian Reichel 	}
763dc7bf5d7SSebastian Reichel 	ssip_set_txstate(ssi, SEND_READY);
764866dcb9dSSebastian Reichel 	spin_unlock_bh(&ssi->lock);
765dc7bf5d7SSebastian Reichel 	ssip_xmit(cl);
766dc7bf5d7SSebastian Reichel }
767dc7bf5d7SSebastian Reichel 
ssip_rx_strans(struct hsi_client * cl,u32 cmd)768dc7bf5d7SSebastian Reichel static void ssip_rx_strans(struct hsi_client *cl, u32 cmd)
769dc7bf5d7SSebastian Reichel {
770dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
771dc7bf5d7SSebastian Reichel 	struct sk_buff *skb;
772dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
773dc7bf5d7SSebastian Reichel 	int len = SSIP_PDU_LENGTH(cmd);
774dc7bf5d7SSebastian Reichel 
775dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "RX strans: %d frames\n", len);
776866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
777dc7bf5d7SSebastian Reichel 	if (unlikely(ssi->main_state != ACTIVE)) {
778dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "START TRANS wrong state: S(%d) M(%d)\n",
779dc7bf5d7SSebastian Reichel 					ssi->send_state, ssi->main_state);
780866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
781dc7bf5d7SSebastian Reichel 		return;
782dc7bf5d7SSebastian Reichel 	}
783dc7bf5d7SSebastian Reichel 	ssip_set_rxstate(ssi, RECEIVING);
784dc7bf5d7SSebastian Reichel 	if (unlikely(SSIP_MSG_ID(cmd) != ssi->rxid)) {
785c616ac28SJakub Wilk 		dev_err(&cl->device, "START TRANS id %d expected %d\n",
786dc7bf5d7SSebastian Reichel 					SSIP_MSG_ID(cmd), ssi->rxid);
787866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
788dc7bf5d7SSebastian Reichel 		goto out1;
789dc7bf5d7SSebastian Reichel 	}
790dc7bf5d7SSebastian Reichel 	ssi->rxid++;
791866dcb9dSSebastian Reichel 	spin_unlock_bh(&ssi->lock);
792dc7bf5d7SSebastian Reichel 	skb = netdev_alloc_skb(ssi->netdev, len * 4);
793dc7bf5d7SSebastian Reichel 	if (unlikely(!skb)) {
794dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "No memory for rx skb\n");
795dc7bf5d7SSebastian Reichel 		goto out1;
796dc7bf5d7SSebastian Reichel 	}
797dc7bf5d7SSebastian Reichel 	skb_put(skb, len * 4);
798dc7bf5d7SSebastian Reichel 	msg = ssip_alloc_data(ssi, skb, GFP_ATOMIC);
799dc7bf5d7SSebastian Reichel 	if (unlikely(!msg)) {
800dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "No memory for RX data msg\n");
801dc7bf5d7SSebastian Reichel 		goto out2;
802dc7bf5d7SSebastian Reichel 	}
803dc7bf5d7SSebastian Reichel 	msg->complete = ssip_rx_data_complete;
804dc7bf5d7SSebastian Reichel 	hsi_async_read(cl, msg);
805dc7bf5d7SSebastian Reichel 
806dc7bf5d7SSebastian Reichel 	return;
807dc7bf5d7SSebastian Reichel out2:
808dc7bf5d7SSebastian Reichel 	dev_kfree_skb(skb);
809dc7bf5d7SSebastian Reichel out1:
810dc7bf5d7SSebastian Reichel 	ssip_error(cl);
811dc7bf5d7SSebastian Reichel }
812dc7bf5d7SSebastian Reichel 
ssip_rxcmd_complete(struct hsi_msg * msg)813dc7bf5d7SSebastian Reichel static void ssip_rxcmd_complete(struct hsi_msg *msg)
814dc7bf5d7SSebastian Reichel {
815dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = msg->cl;
816dc7bf5d7SSebastian Reichel 	u32 cmd = ssip_get_cmd(msg);
817dc7bf5d7SSebastian Reichel 	unsigned int cmdid = SSIP_COMMAND(cmd);
818dc7bf5d7SSebastian Reichel 
819dc7bf5d7SSebastian Reichel 	if (msg->status == HSI_STATUS_ERROR) {
820dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "RX error detected\n");
821dc7bf5d7SSebastian Reichel 		ssip_release_cmd(msg);
822dc7bf5d7SSebastian Reichel 		ssip_error(cl);
823dc7bf5d7SSebastian Reichel 		return;
824dc7bf5d7SSebastian Reichel 	}
825dc7bf5d7SSebastian Reichel 	hsi_async_read(cl, msg);
826dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "RX cmd: 0x%08x\n", cmd);
827dc7bf5d7SSebastian Reichel 	switch (cmdid) {
828dc7bf5d7SSebastian Reichel 	case SSIP_SW_BREAK:
829dc7bf5d7SSebastian Reichel 		/* Ignored */
830dc7bf5d7SSebastian Reichel 		break;
831dc7bf5d7SSebastian Reichel 	case SSIP_BOOTINFO_REQ:
832dc7bf5d7SSebastian Reichel 		ssip_rx_bootinforeq(cl, cmd);
833dc7bf5d7SSebastian Reichel 		break;
834dc7bf5d7SSebastian Reichel 	case SSIP_BOOTINFO_RESP:
835dc7bf5d7SSebastian Reichel 		ssip_rx_bootinforesp(cl, cmd);
836dc7bf5d7SSebastian Reichel 		break;
837dc7bf5d7SSebastian Reichel 	case SSIP_WAKETEST_RESULT:
838dc7bf5d7SSebastian Reichel 		ssip_rx_waketest(cl, cmd);
839dc7bf5d7SSebastian Reichel 		break;
840dc7bf5d7SSebastian Reichel 	case SSIP_START_TRANS:
841dc7bf5d7SSebastian Reichel 		ssip_rx_strans(cl, cmd);
842dc7bf5d7SSebastian Reichel 		break;
843dc7bf5d7SSebastian Reichel 	case SSIP_READY:
844dc7bf5d7SSebastian Reichel 		ssip_rx_ready(cl);
845dc7bf5d7SSebastian Reichel 		break;
846dc7bf5d7SSebastian Reichel 	default:
847dc7bf5d7SSebastian Reichel 		dev_warn(&cl->device, "command 0x%08x not supported\n", cmd);
848dc7bf5d7SSebastian Reichel 		break;
849dc7bf5d7SSebastian Reichel 	}
850dc7bf5d7SSebastian Reichel }
851dc7bf5d7SSebastian Reichel 
ssip_swbreak_complete(struct hsi_msg * msg)852dc7bf5d7SSebastian Reichel static void ssip_swbreak_complete(struct hsi_msg *msg)
853dc7bf5d7SSebastian Reichel {
854dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = msg->cl;
855dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
856dc7bf5d7SSebastian Reichel 
857dc7bf5d7SSebastian Reichel 	ssip_release_cmd(msg);
858866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
859dc7bf5d7SSebastian Reichel 	if (list_empty(&ssi->txqueue)) {
860dc7bf5d7SSebastian Reichel 		if (atomic_read(&ssi->tx_usecnt)) {
861dc7bf5d7SSebastian Reichel 			ssip_set_txstate(ssi, SEND_READY);
862dc7bf5d7SSebastian Reichel 		} else {
863dc7bf5d7SSebastian Reichel 			ssip_set_txstate(ssi, SEND_IDLE);
864dc7bf5d7SSebastian Reichel 			hsi_stop_tx(cl);
865dc7bf5d7SSebastian Reichel 		}
866866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
867dc7bf5d7SSebastian Reichel 	} else {
868866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
869dc7bf5d7SSebastian Reichel 		ssip_xmit(cl);
870dc7bf5d7SSebastian Reichel 	}
871dc7bf5d7SSebastian Reichel 	netif_wake_queue(ssi->netdev);
872dc7bf5d7SSebastian Reichel }
873dc7bf5d7SSebastian Reichel 
ssip_tx_data_complete(struct hsi_msg * msg)874dc7bf5d7SSebastian Reichel static void ssip_tx_data_complete(struct hsi_msg *msg)
875dc7bf5d7SSebastian Reichel {
876dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = msg->cl;
877dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
878dc7bf5d7SSebastian Reichel 	struct hsi_msg *cmsg;
879dc7bf5d7SSebastian Reichel 
880dc7bf5d7SSebastian Reichel 	if (msg->status == HSI_STATUS_ERROR) {
881dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "TX data error\n");
882dc7bf5d7SSebastian Reichel 		ssip_error(cl);
883dc7bf5d7SSebastian Reichel 		goto out;
884dc7bf5d7SSebastian Reichel 	}
885866dcb9dSSebastian Reichel 	spin_lock_bh(&ssi->lock);
886dc7bf5d7SSebastian Reichel 	if (list_empty(&ssi->txqueue)) {
887dc7bf5d7SSebastian Reichel 		ssip_set_txstate(ssi, SENDING_SWBREAK);
888866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
889dc7bf5d7SSebastian Reichel 		cmsg = ssip_claim_cmd(ssi);
890dc7bf5d7SSebastian Reichel 		ssip_set_cmd(cmsg, SSIP_SWBREAK_CMD);
891dc7bf5d7SSebastian Reichel 		cmsg->complete = ssip_swbreak_complete;
892dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "Send SWBREAK\n");
893dc7bf5d7SSebastian Reichel 		hsi_async_write(cl, cmsg);
894dc7bf5d7SSebastian Reichel 	} else {
895866dcb9dSSebastian Reichel 		spin_unlock_bh(&ssi->lock);
896dc7bf5d7SSebastian Reichel 		ssip_xmit(cl);
897dc7bf5d7SSebastian Reichel 	}
898dc7bf5d7SSebastian Reichel out:
899dc7bf5d7SSebastian Reichel 	ssip_free_data(msg);
900dc7bf5d7SSebastian Reichel }
901dc7bf5d7SSebastian Reichel 
ssip_port_event(struct hsi_client * cl,unsigned long event)9020a0ea07dSWei Yongjun static void ssip_port_event(struct hsi_client *cl, unsigned long event)
903dc7bf5d7SSebastian Reichel {
904dc7bf5d7SSebastian Reichel 	switch (event) {
905dc7bf5d7SSebastian Reichel 	case HSI_EVENT_START_RX:
906dc7bf5d7SSebastian Reichel 		ssip_start_rx(cl);
907dc7bf5d7SSebastian Reichel 		break;
908dc7bf5d7SSebastian Reichel 	case HSI_EVENT_STOP_RX:
909dc7bf5d7SSebastian Reichel 		ssip_stop_rx(cl);
910dc7bf5d7SSebastian Reichel 		break;
911dc7bf5d7SSebastian Reichel 	default:
912dc7bf5d7SSebastian Reichel 		return;
913dc7bf5d7SSebastian Reichel 	}
914dc7bf5d7SSebastian Reichel }
915dc7bf5d7SSebastian Reichel 
ssip_pn_open(struct net_device * dev)916dc7bf5d7SSebastian Reichel static int ssip_pn_open(struct net_device *dev)
917dc7bf5d7SSebastian Reichel {
918dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = to_hsi_client(dev->dev.parent);
919dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
920dc7bf5d7SSebastian Reichel 	int err;
921dc7bf5d7SSebastian Reichel 
922dc7bf5d7SSebastian Reichel 	err = hsi_claim_port(cl, 1);
923dc7bf5d7SSebastian Reichel 	if (err < 0) {
924dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "SSI port already claimed\n");
925dc7bf5d7SSebastian Reichel 		return err;
926dc7bf5d7SSebastian Reichel 	}
927dc7bf5d7SSebastian Reichel 	err = hsi_register_port_event(cl, ssip_port_event);
928dc7bf5d7SSebastian Reichel 	if (err < 0) {
929dc7bf5d7SSebastian Reichel 		dev_err(&cl->device, "Register HSI port event failed (%d)\n",
930dc7bf5d7SSebastian Reichel 			err);
931b28dbcb3SJianglei Nie 		hsi_release_port(cl);
932dc7bf5d7SSebastian Reichel 		return err;
933dc7bf5d7SSebastian Reichel 	}
934dc7bf5d7SSebastian Reichel 	dev_dbg(&cl->device, "Configuring SSI port\n");
935dc7bf5d7SSebastian Reichel 	hsi_setup(cl);
9366d6c3097SSebastian Reichel 
9376d6c3097SSebastian Reichel 	if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
938dc7bf5d7SSebastian Reichel 		ssi_waketest(cl, 1); /* FIXME: To be removed */
9396d6c3097SSebastian Reichel 
9406d6c3097SSebastian Reichel 	spin_lock_bh(&ssi->lock);
94187d99063SSebastian Reichel 	ssi->main_state = HANDSHAKE;
942dc7bf5d7SSebastian Reichel 	spin_unlock_bh(&ssi->lock);
943dc7bf5d7SSebastian Reichel 
94487d99063SSebastian Reichel 	ssip_send_bootinfo_req_cmd(cl);
94587d99063SSebastian Reichel 
946dc7bf5d7SSebastian Reichel 	return 0;
947dc7bf5d7SSebastian Reichel }
948dc7bf5d7SSebastian Reichel 
ssip_pn_stop(struct net_device * dev)949dc7bf5d7SSebastian Reichel static int ssip_pn_stop(struct net_device *dev)
950dc7bf5d7SSebastian Reichel {
951dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = to_hsi_client(dev->dev.parent);
952dc7bf5d7SSebastian Reichel 
953dc7bf5d7SSebastian Reichel 	ssip_reset(cl);
954dc7bf5d7SSebastian Reichel 	hsi_unregister_port_event(cl);
955dc7bf5d7SSebastian Reichel 	hsi_release_port(cl);
956dc7bf5d7SSebastian Reichel 
957dc7bf5d7SSebastian Reichel 	return 0;
958dc7bf5d7SSebastian Reichel }
959dc7bf5d7SSebastian Reichel 
ssip_xmit_work(struct work_struct * work)960df26d639SSebastian Reichel static void ssip_xmit_work(struct work_struct *work)
961df26d639SSebastian Reichel {
962df26d639SSebastian Reichel 	struct ssi_protocol *ssi =
963df26d639SSebastian Reichel 				container_of(work, struct ssi_protocol, work);
964df26d639SSebastian Reichel 	struct hsi_client *cl = ssi->cl;
965df26d639SSebastian Reichel 
966df26d639SSebastian Reichel 	ssip_xmit(cl);
967df26d639SSebastian Reichel }
968df26d639SSebastian Reichel 
ssip_pn_xmit(struct sk_buff * skb,struct net_device * dev)969*913a1441SNathan Chancellor static netdev_tx_t ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev)
970dc7bf5d7SSebastian Reichel {
971dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = to_hsi_client(dev->dev.parent);
972dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
973dc7bf5d7SSebastian Reichel 	struct hsi_msg *msg;
974dc7bf5d7SSebastian Reichel 
975dc7bf5d7SSebastian Reichel 	if ((skb->protocol != htons(ETH_P_PHONET)) ||
976dc7bf5d7SSebastian Reichel 					(skb->len < SSIP_MIN_PN_HDR))
977dc7bf5d7SSebastian Reichel 		goto drop;
978dc7bf5d7SSebastian Reichel 	/* Pad to 32-bits - FIXME: Revisit*/
979dc7bf5d7SSebastian Reichel 	if ((skb->len & 3) && skb_pad(skb, 4 - (skb->len & 3)))
98030260501SDan Carpenter 		goto inc_dropped;
981dc7bf5d7SSebastian Reichel 
982dc7bf5d7SSebastian Reichel 	/*
98380fb8a85SMarkus Elfring 	 * Modem sends Phonet messages over SSI with its own endianness.
98480fb8a85SMarkus Elfring 	 * Assume that modem has the same endianness as we do.
985dc7bf5d7SSebastian Reichel 	 */
986dc7bf5d7SSebastian Reichel 	if (skb_cow_head(skb, 0))
987dc7bf5d7SSebastian Reichel 		goto drop;
988dc7bf5d7SSebastian Reichel 
989dc7bf5d7SSebastian Reichel 	/* length field is exchanged in network byte order */
990dc7bf5d7SSebastian Reichel 	((u16 *)skb->data)[2] = htons(((u16 *)skb->data)[2]);
991dc7bf5d7SSebastian Reichel 
992dc7bf5d7SSebastian Reichel 	msg = ssip_alloc_data(ssi, skb, GFP_ATOMIC);
993dc7bf5d7SSebastian Reichel 	if (!msg) {
994dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "Dropping tx data: No memory\n");
995dc7bf5d7SSebastian Reichel 		goto drop;
996dc7bf5d7SSebastian Reichel 	}
997dc7bf5d7SSebastian Reichel 	msg->complete = ssip_tx_data_complete;
998dc7bf5d7SSebastian Reichel 
999dc7bf5d7SSebastian Reichel 	spin_lock_bh(&ssi->lock);
1000dc7bf5d7SSebastian Reichel 	if (unlikely(ssi->main_state != ACTIVE)) {
1001dc7bf5d7SSebastian Reichel 		spin_unlock_bh(&ssi->lock);
1002dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "Dropping tx data: CMT is OFFLINE\n");
1003dc7bf5d7SSebastian Reichel 		goto drop2;
1004dc7bf5d7SSebastian Reichel 	}
1005dc7bf5d7SSebastian Reichel 	list_add_tail(&msg->link, &ssi->txqueue);
1006dc7bf5d7SSebastian Reichel 	ssi->txqueue_len++;
1007dc7bf5d7SSebastian Reichel 	if (dev->tx_queue_len < ssi->txqueue_len) {
1008dc7bf5d7SSebastian Reichel 		dev_info(&cl->device, "TX queue full %d\n", ssi->txqueue_len);
1009dc7bf5d7SSebastian Reichel 		netif_stop_queue(dev);
1010dc7bf5d7SSebastian Reichel 	}
1011dc7bf5d7SSebastian Reichel 	if (ssi->send_state == SEND_IDLE) {
1012dc7bf5d7SSebastian Reichel 		ssip_set_txstate(ssi, WAIT4READY);
1013dc7bf5d7SSebastian Reichel 		spin_unlock_bh(&ssi->lock);
1014dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "Start TX qlen %d\n", ssi->txqueue_len);
1015dc7bf5d7SSebastian Reichel 		hsi_start_tx(cl);
1016dc7bf5d7SSebastian Reichel 	} else if (ssi->send_state == SEND_READY) {
1017dc7bf5d7SSebastian Reichel 		/* Needed for cmt-speech workaround */
1018dc7bf5d7SSebastian Reichel 		dev_dbg(&cl->device, "Start TX on SEND READY qlen %d\n",
1019dc7bf5d7SSebastian Reichel 							ssi->txqueue_len);
1020dc7bf5d7SSebastian Reichel 		spin_unlock_bh(&ssi->lock);
1021df26d639SSebastian Reichel 		schedule_work(&ssi->work);
1022dc7bf5d7SSebastian Reichel 	} else {
1023dc7bf5d7SSebastian Reichel 		spin_unlock_bh(&ssi->lock);
1024dc7bf5d7SSebastian Reichel 	}
1025dc7bf5d7SSebastian Reichel 	dev->stats.tx_packets++;
1026dc7bf5d7SSebastian Reichel 	dev->stats.tx_bytes += skb->len;
1027dc7bf5d7SSebastian Reichel 
1028*913a1441SNathan Chancellor 	return NETDEV_TX_OK;
1029dc7bf5d7SSebastian Reichel drop2:
1030dc7bf5d7SSebastian Reichel 	hsi_free_msg(msg);
1031dc7bf5d7SSebastian Reichel drop:
1032dc7bf5d7SSebastian Reichel 	dev_kfree_skb(skb);
103330260501SDan Carpenter inc_dropped:
103430260501SDan Carpenter 	dev->stats.tx_dropped++;
1035dc7bf5d7SSebastian Reichel 
1036*913a1441SNathan Chancellor 	return NETDEV_TX_OK;
1037dc7bf5d7SSebastian Reichel }
1038dc7bf5d7SSebastian Reichel 
1039dc7bf5d7SSebastian Reichel /* CMT reset event handler */
ssip_reset_event(struct hsi_client * master)1040dc7bf5d7SSebastian Reichel void ssip_reset_event(struct hsi_client *master)
1041dc7bf5d7SSebastian Reichel {
1042dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(master);
1043dc7bf5d7SSebastian Reichel 	dev_err(&ssi->cl->device, "CMT reset detected!\n");
1044dc7bf5d7SSebastian Reichel 	ssip_error(ssi->cl);
1045dc7bf5d7SSebastian Reichel }
1046dc7bf5d7SSebastian Reichel EXPORT_SYMBOL_GPL(ssip_reset_event);
1047dc7bf5d7SSebastian Reichel 
1048dc7bf5d7SSebastian Reichel static const struct net_device_ops ssip_pn_ops = {
1049dc7bf5d7SSebastian Reichel 	.ndo_open	= ssip_pn_open,
1050dc7bf5d7SSebastian Reichel 	.ndo_stop	= ssip_pn_stop,
1051dc7bf5d7SSebastian Reichel 	.ndo_start_xmit	= ssip_pn_xmit,
1052dc7bf5d7SSebastian Reichel };
1053dc7bf5d7SSebastian Reichel 
ssip_pn_setup(struct net_device * dev)1054dc7bf5d7SSebastian Reichel static void ssip_pn_setup(struct net_device *dev)
1055dc7bf5d7SSebastian Reichel {
105613b5ffa0SJakub Kicinski 	static const u8 addr = PN_MEDIA_SOS;
105713b5ffa0SJakub Kicinski 
1058dc7bf5d7SSebastian Reichel 	dev->features		= 0;
1059dc7bf5d7SSebastian Reichel 	dev->netdev_ops		= &ssip_pn_ops;
1060dc7bf5d7SSebastian Reichel 	dev->type		= ARPHRD_PHONET;
1061dc7bf5d7SSebastian Reichel 	dev->flags		= IFF_POINTOPOINT | IFF_NOARP;
1062dc7bf5d7SSebastian Reichel 	dev->mtu		= SSIP_DEFAULT_MTU;
1063dc7bf5d7SSebastian Reichel 	dev->hard_header_len	= 1;
1064dc7bf5d7SSebastian Reichel 	dev->addr_len		= 1;
106513b5ffa0SJakub Kicinski 	dev_addr_set(dev, &addr);
1066dc7bf5d7SSebastian Reichel 	dev->tx_queue_len	= SSIP_TXQUEUE_LEN;
1067dc7bf5d7SSebastian Reichel 
1068ed66e50dSDavid S. Miller 	dev->needs_free_netdev	= true;
1069dc7bf5d7SSebastian Reichel 	dev->header_ops		= &phonet_header_ops;
1070dc7bf5d7SSebastian Reichel }
1071dc7bf5d7SSebastian Reichel 
ssi_protocol_probe(struct device * dev)1072dc7bf5d7SSebastian Reichel static int ssi_protocol_probe(struct device *dev)
1073dc7bf5d7SSebastian Reichel {
1074dc7bf5d7SSebastian Reichel 	static const char ifname[] = "phonet%d";
1075dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = to_hsi_client(dev);
1076dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi;
1077dc7bf5d7SSebastian Reichel 	int err;
1078dc7bf5d7SSebastian Reichel 
1079dc7bf5d7SSebastian Reichel 	ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
10802d8236d1SMarkus Elfring 	if (!ssi)
1081dc7bf5d7SSebastian Reichel 		return -ENOMEM;
1082dc7bf5d7SSebastian Reichel 
1083dc7bf5d7SSebastian Reichel 	spin_lock_init(&ssi->lock);
1084df7e828cSKees Cook 	timer_setup(&ssi->rx_wd, ssip_rx_wd, TIMER_DEFERRABLE);
1085df7e828cSKees Cook 	timer_setup(&ssi->tx_wd, ssip_tx_wd, TIMER_DEFERRABLE);
1086df7e828cSKees Cook 	timer_setup(&ssi->keep_alive, ssip_keep_alive, 0);
1087dc7bf5d7SSebastian Reichel 	INIT_LIST_HEAD(&ssi->txqueue);
1088dc7bf5d7SSebastian Reichel 	INIT_LIST_HEAD(&ssi->cmdqueue);
1089dc7bf5d7SSebastian Reichel 	atomic_set(&ssi->tx_usecnt, 0);
1090dc7bf5d7SSebastian Reichel 	hsi_client_set_drvdata(cl, ssi);
1091dc7bf5d7SSebastian Reichel 	ssi->cl = cl;
1092df26d639SSebastian Reichel 	INIT_WORK(&ssi->work, ssip_xmit_work);
1093dc7bf5d7SSebastian Reichel 
1094dc7bf5d7SSebastian Reichel 	ssi->channel_id_cmd = hsi_get_channel_id_by_name(cl, "mcsaab-control");
1095dc7bf5d7SSebastian Reichel 	if (ssi->channel_id_cmd < 0) {
1096dc7bf5d7SSebastian Reichel 		err = ssi->channel_id_cmd;
1097dc7bf5d7SSebastian Reichel 		dev_err(dev, "Could not get cmd channel (%d)\n", err);
1098dc7bf5d7SSebastian Reichel 		goto out;
1099dc7bf5d7SSebastian Reichel 	}
1100dc7bf5d7SSebastian Reichel 
1101dc7bf5d7SSebastian Reichel 	ssi->channel_id_data = hsi_get_channel_id_by_name(cl, "mcsaab-data");
1102dc7bf5d7SSebastian Reichel 	if (ssi->channel_id_data < 0) {
1103dc7bf5d7SSebastian Reichel 		err = ssi->channel_id_data;
1104dc7bf5d7SSebastian Reichel 		dev_err(dev, "Could not get data channel (%d)\n", err);
1105dc7bf5d7SSebastian Reichel 		goto out;
1106dc7bf5d7SSebastian Reichel 	}
1107dc7bf5d7SSebastian Reichel 
1108dc7bf5d7SSebastian Reichel 	err = ssip_alloc_cmds(ssi);
1109dc7bf5d7SSebastian Reichel 	if (err < 0) {
1110dc7bf5d7SSebastian Reichel 		dev_err(dev, "No memory for commands\n");
1111dc7bf5d7SSebastian Reichel 		goto out;
1112dc7bf5d7SSebastian Reichel 	}
1113dc7bf5d7SSebastian Reichel 
1114c835a677STom Gundersen 	ssi->netdev = alloc_netdev(0, ifname, NET_NAME_UNKNOWN, ssip_pn_setup);
1115dc7bf5d7SSebastian Reichel 	if (!ssi->netdev) {
1116dc7bf5d7SSebastian Reichel 		dev_err(dev, "No memory for netdev\n");
1117dc7bf5d7SSebastian Reichel 		err = -ENOMEM;
1118dc7bf5d7SSebastian Reichel 		goto out1;
1119dc7bf5d7SSebastian Reichel 	}
1120dc7bf5d7SSebastian Reichel 
1121b3e3893eSJarod Wilson 	/* MTU range: 6 - 65535 */
1122b3e3893eSJarod Wilson 	ssi->netdev->min_mtu = PHONET_MIN_MTU;
1123b3e3893eSJarod Wilson 	ssi->netdev->max_mtu = SSIP_MAX_MTU;
1124b3e3893eSJarod Wilson 
1125dc7bf5d7SSebastian Reichel 	SET_NETDEV_DEV(ssi->netdev, dev);
1126dc7bf5d7SSebastian Reichel 	netif_carrier_off(ssi->netdev);
1127dc7bf5d7SSebastian Reichel 	err = register_netdev(ssi->netdev);
1128dc7bf5d7SSebastian Reichel 	if (err < 0) {
1129dc7bf5d7SSebastian Reichel 		dev_err(dev, "Register netdev failed (%d)\n", err);
1130dc7bf5d7SSebastian Reichel 		goto out2;
1131dc7bf5d7SSebastian Reichel 	}
1132dc7bf5d7SSebastian Reichel 
1133dc7bf5d7SSebastian Reichel 	list_add(&ssi->link, &ssip_list);
1134dc7bf5d7SSebastian Reichel 
1135dc7bf5d7SSebastian Reichel 	dev_dbg(dev, "channel configuration: cmd=%d, data=%d\n",
1136dc7bf5d7SSebastian Reichel 		ssi->channel_id_cmd, ssi->channel_id_data);
1137dc7bf5d7SSebastian Reichel 
1138dc7bf5d7SSebastian Reichel 	return 0;
1139dc7bf5d7SSebastian Reichel out2:
1140dc7bf5d7SSebastian Reichel 	free_netdev(ssi->netdev);
1141dc7bf5d7SSebastian Reichel out1:
1142dc7bf5d7SSebastian Reichel 	ssip_free_cmds(ssi);
1143dc7bf5d7SSebastian Reichel out:
1144dc7bf5d7SSebastian Reichel 	kfree(ssi);
1145dc7bf5d7SSebastian Reichel 
1146dc7bf5d7SSebastian Reichel 	return err;
1147dc7bf5d7SSebastian Reichel }
1148dc7bf5d7SSebastian Reichel 
ssi_protocol_remove(struct device * dev)1149dc7bf5d7SSebastian Reichel static int ssi_protocol_remove(struct device *dev)
1150dc7bf5d7SSebastian Reichel {
1151dc7bf5d7SSebastian Reichel 	struct hsi_client *cl = to_hsi_client(dev);
1152dc7bf5d7SSebastian Reichel 	struct ssi_protocol *ssi = hsi_client_drvdata(cl);
1153dc7bf5d7SSebastian Reichel 
1154dc7bf5d7SSebastian Reichel 	list_del(&ssi->link);
1155dc7bf5d7SSebastian Reichel 	unregister_netdev(ssi->netdev);
1156dc7bf5d7SSebastian Reichel 	ssip_free_cmds(ssi);
1157dc7bf5d7SSebastian Reichel 	hsi_client_set_drvdata(cl, NULL);
1158dc7bf5d7SSebastian Reichel 	kfree(ssi);
1159dc7bf5d7SSebastian Reichel 
1160dc7bf5d7SSebastian Reichel 	return 0;
1161dc7bf5d7SSebastian Reichel }
1162dc7bf5d7SSebastian Reichel 
1163dc7bf5d7SSebastian Reichel static struct hsi_client_driver ssip_driver = {
1164dc7bf5d7SSebastian Reichel 	.driver = {
1165dc7bf5d7SSebastian Reichel 		.name	= "ssi-protocol",
1166dc7bf5d7SSebastian Reichel 		.owner	= THIS_MODULE,
1167dc7bf5d7SSebastian Reichel 		.probe	= ssi_protocol_probe,
1168dc7bf5d7SSebastian Reichel 		.remove	= ssi_protocol_remove,
1169dc7bf5d7SSebastian Reichel 	},
1170dc7bf5d7SSebastian Reichel };
1171dc7bf5d7SSebastian Reichel 
ssip_init(void)1172dc7bf5d7SSebastian Reichel static int __init ssip_init(void)
1173dc7bf5d7SSebastian Reichel {
1174dc7bf5d7SSebastian Reichel 	pr_info("SSI protocol aka McSAAB added\n");
1175dc7bf5d7SSebastian Reichel 
1176dc7bf5d7SSebastian Reichel 	return hsi_register_client_driver(&ssip_driver);
1177dc7bf5d7SSebastian Reichel }
1178dc7bf5d7SSebastian Reichel module_init(ssip_init);
1179dc7bf5d7SSebastian Reichel 
ssip_exit(void)1180dc7bf5d7SSebastian Reichel static void __exit ssip_exit(void)
1181dc7bf5d7SSebastian Reichel {
1182dc7bf5d7SSebastian Reichel 	hsi_unregister_client_driver(&ssip_driver);
1183dc7bf5d7SSebastian Reichel 	pr_info("SSI protocol driver removed\n");
1184dc7bf5d7SSebastian Reichel }
1185dc7bf5d7SSebastian Reichel module_exit(ssip_exit);
1186dc7bf5d7SSebastian Reichel 
1187dc7bf5d7SSebastian Reichel MODULE_ALIAS("hsi:ssi-protocol");
1188dc7bf5d7SSebastian Reichel MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
1189dc7bf5d7SSebastian Reichel MODULE_AUTHOR("Remi Denis-Courmont <remi.denis-courmont@nokia.com>");
1190dc7bf5d7SSebastian Reichel MODULE_DESCRIPTION("SSI protocol improved aka McSAAB");
1191dc7bf5d7SSebastian Reichel MODULE_LICENSE("GPL");
1192