xref: /OK3568_Linux_fs/kernel/drivers/hid/intel-ish-hid/ipc/ipc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * H/W layer of ISHTP provider device (ISH)
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2014-2016, Intel Corporation.
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/sched.h>
9*4882a593Smuzhiyun #include <linux/spinlock.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/jiffies.h>
12*4882a593Smuzhiyun #include "client.h"
13*4882a593Smuzhiyun #include "hw-ish.h"
14*4882a593Smuzhiyun #include "hbm.h"
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun /* For FW reset flow */
17*4882a593Smuzhiyun static struct work_struct fw_reset_work;
18*4882a593Smuzhiyun static struct ishtp_device *ishtp_dev;
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun /**
21*4882a593Smuzhiyun  * ish_reg_read() - Read register
22*4882a593Smuzhiyun  * @dev: ISHTP device pointer
23*4882a593Smuzhiyun  * @offset: Register offset
24*4882a593Smuzhiyun  *
25*4882a593Smuzhiyun  * Read 32 bit register at a given offset
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  * Return: Read register value
28*4882a593Smuzhiyun  */
ish_reg_read(const struct ishtp_device * dev,unsigned long offset)29*4882a593Smuzhiyun static inline uint32_t ish_reg_read(const struct ishtp_device *dev,
30*4882a593Smuzhiyun 	unsigned long offset)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	struct ish_hw *hw = to_ish_hw(dev);
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun 	return readl(hw->mem_addr + offset);
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /**
38*4882a593Smuzhiyun  * ish_reg_write() - Write register
39*4882a593Smuzhiyun  * @dev: ISHTP device pointer
40*4882a593Smuzhiyun  * @offset: Register offset
41*4882a593Smuzhiyun  * @value: Value to write
42*4882a593Smuzhiyun  *
43*4882a593Smuzhiyun  * Writes 32 bit register at a give offset
44*4882a593Smuzhiyun  */
ish_reg_write(struct ishtp_device * dev,unsigned long offset,uint32_t value)45*4882a593Smuzhiyun static inline void ish_reg_write(struct ishtp_device *dev,
46*4882a593Smuzhiyun 				 unsigned long offset,
47*4882a593Smuzhiyun 				 uint32_t value)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun 	struct ish_hw *hw = to_ish_hw(dev);
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	writel(value, hw->mem_addr + offset);
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun /**
55*4882a593Smuzhiyun  * _ish_read_fw_sts_reg() - Read FW status register
56*4882a593Smuzhiyun  * @dev: ISHTP device pointer
57*4882a593Smuzhiyun  *
58*4882a593Smuzhiyun  * Read FW status register
59*4882a593Smuzhiyun  *
60*4882a593Smuzhiyun  * Return: Read register value
61*4882a593Smuzhiyun  */
_ish_read_fw_sts_reg(struct ishtp_device * dev)62*4882a593Smuzhiyun static inline uint32_t _ish_read_fw_sts_reg(struct ishtp_device *dev)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun 	return ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun /**
68*4882a593Smuzhiyun  * check_generated_interrupt() - Check if ISH interrupt
69*4882a593Smuzhiyun  * @dev: ISHTP device pointer
70*4882a593Smuzhiyun  *
71*4882a593Smuzhiyun  * Check if an interrupt was generated for ISH
72*4882a593Smuzhiyun  *
73*4882a593Smuzhiyun  * Return: Read true or false
74*4882a593Smuzhiyun  */
check_generated_interrupt(struct ishtp_device * dev)75*4882a593Smuzhiyun static bool check_generated_interrupt(struct ishtp_device *dev)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	bool interrupt_generated = true;
78*4882a593Smuzhiyun 	uint32_t pisr_val = 0;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	if (dev->pdev->device == CHV_DEVICE_ID) {
81*4882a593Smuzhiyun 		pisr_val = ish_reg_read(dev, IPC_REG_PISR_CHV_AB);
82*4882a593Smuzhiyun 		interrupt_generated =
83*4882a593Smuzhiyun 			IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val);
84*4882a593Smuzhiyun 	} else {
85*4882a593Smuzhiyun 		pisr_val = ish_reg_read(dev, IPC_REG_PISR_BXT);
86*4882a593Smuzhiyun 		interrupt_generated = !!pisr_val;
87*4882a593Smuzhiyun 		/* only busy-clear bit is RW, others are RO */
88*4882a593Smuzhiyun 		if (pisr_val)
89*4882a593Smuzhiyun 			ish_reg_write(dev, IPC_REG_PISR_BXT, pisr_val);
90*4882a593Smuzhiyun 	}
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	return interrupt_generated;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun /**
96*4882a593Smuzhiyun  * ish_is_input_ready() - Check if FW ready for RX
97*4882a593Smuzhiyun  * @dev: ISHTP device pointer
98*4882a593Smuzhiyun  *
99*4882a593Smuzhiyun  * Check if ISH FW is ready for receiving data
100*4882a593Smuzhiyun  *
101*4882a593Smuzhiyun  * Return: Read true or false
102*4882a593Smuzhiyun  */
ish_is_input_ready(struct ishtp_device * dev)103*4882a593Smuzhiyun static bool ish_is_input_ready(struct ishtp_device *dev)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun 	uint32_t doorbell_val;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	doorbell_val = ish_reg_read(dev, IPC_REG_HOST2ISH_DRBL);
108*4882a593Smuzhiyun 	return !IPC_IS_BUSY(doorbell_val);
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun /**
112*4882a593Smuzhiyun  * set_host_ready() - Indicate host ready
113*4882a593Smuzhiyun  * @dev: ISHTP device pointer
114*4882a593Smuzhiyun  *
115*4882a593Smuzhiyun  * Set host ready indication to FW
116*4882a593Smuzhiyun  */
set_host_ready(struct ishtp_device * dev)117*4882a593Smuzhiyun static void set_host_ready(struct ishtp_device *dev)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	if (dev->pdev->device == CHV_DEVICE_ID) {
120*4882a593Smuzhiyun 		if (dev->pdev->revision == REVISION_ID_CHT_A0 ||
121*4882a593Smuzhiyun 				(dev->pdev->revision & REVISION_ID_SI_MASK) ==
122*4882a593Smuzhiyun 				REVISION_ID_CHT_Ax_SI)
123*4882a593Smuzhiyun 			ish_reg_write(dev, IPC_REG_HOST_COMM, 0x81);
124*4882a593Smuzhiyun 		else if (dev->pdev->revision == REVISION_ID_CHT_B0 ||
125*4882a593Smuzhiyun 				(dev->pdev->revision & REVISION_ID_SI_MASK) ==
126*4882a593Smuzhiyun 				REVISION_ID_CHT_Bx_SI ||
127*4882a593Smuzhiyun 				(dev->pdev->revision & REVISION_ID_SI_MASK) ==
128*4882a593Smuzhiyun 				REVISION_ID_CHT_Kx_SI ||
129*4882a593Smuzhiyun 				(dev->pdev->revision & REVISION_ID_SI_MASK) ==
130*4882a593Smuzhiyun 				REVISION_ID_CHT_Dx_SI) {
131*4882a593Smuzhiyun 			uint32_t host_comm_val;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 			host_comm_val = ish_reg_read(dev, IPC_REG_HOST_COMM);
134*4882a593Smuzhiyun 			host_comm_val |= IPC_HOSTCOMM_INT_EN_BIT_CHV_AB | 0x81;
135*4882a593Smuzhiyun 			ish_reg_write(dev, IPC_REG_HOST_COMM, host_comm_val);
136*4882a593Smuzhiyun 		}
137*4882a593Smuzhiyun 	} else {
138*4882a593Smuzhiyun 			uint32_t host_pimr_val;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 			host_pimr_val = ish_reg_read(dev, IPC_REG_PIMR_BXT);
141*4882a593Smuzhiyun 			host_pimr_val |= IPC_PIMR_INT_EN_BIT_BXT;
142*4882a593Smuzhiyun 			/*
143*4882a593Smuzhiyun 			 * disable interrupt generated instead of
144*4882a593Smuzhiyun 			 * RX_complete_msg
145*4882a593Smuzhiyun 			 */
146*4882a593Smuzhiyun 			host_pimr_val &= ~IPC_HOST2ISH_BUSYCLEAR_MASK_BIT;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 			ish_reg_write(dev, IPC_REG_PIMR_BXT, host_pimr_val);
149*4882a593Smuzhiyun 	}
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun /**
153*4882a593Smuzhiyun  * ishtp_fw_is_ready() - Check if FW ready
154*4882a593Smuzhiyun  * @dev: ISHTP device pointer
155*4882a593Smuzhiyun  *
156*4882a593Smuzhiyun  * Check if ISH FW is ready
157*4882a593Smuzhiyun  *
158*4882a593Smuzhiyun  * Return: Read true or false
159*4882a593Smuzhiyun  */
ishtp_fw_is_ready(struct ishtp_device * dev)160*4882a593Smuzhiyun static bool ishtp_fw_is_ready(struct ishtp_device *dev)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun 	uint32_t ish_status = _ish_read_fw_sts_reg(dev);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	return IPC_IS_ISH_ILUP(ish_status) &&
165*4882a593Smuzhiyun 		IPC_IS_ISH_ISHTP_READY(ish_status);
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun /**
169*4882a593Smuzhiyun  * ish_set_host_rdy() - Indicate host ready
170*4882a593Smuzhiyun  * @dev: ISHTP device pointer
171*4882a593Smuzhiyun  *
172*4882a593Smuzhiyun  * Set host ready indication to FW
173*4882a593Smuzhiyun  */
ish_set_host_rdy(struct ishtp_device * dev)174*4882a593Smuzhiyun static void ish_set_host_rdy(struct ishtp_device *dev)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun 	uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	IPC_SET_HOST_READY(host_status);
179*4882a593Smuzhiyun 	ish_reg_write(dev, IPC_REG_HOST_COMM, host_status);
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun /**
183*4882a593Smuzhiyun  * ish_clr_host_rdy() - Indicate host not ready
184*4882a593Smuzhiyun  * @dev: ISHTP device pointer
185*4882a593Smuzhiyun  *
186*4882a593Smuzhiyun  * Send host not ready indication to FW
187*4882a593Smuzhiyun  */
ish_clr_host_rdy(struct ishtp_device * dev)188*4882a593Smuzhiyun static void ish_clr_host_rdy(struct ishtp_device *dev)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM);
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	IPC_CLEAR_HOST_READY(host_status);
193*4882a593Smuzhiyun 	ish_reg_write(dev, IPC_REG_HOST_COMM, host_status);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun /**
197*4882a593Smuzhiyun  * _ishtp_read_hdr() - Read message header
198*4882a593Smuzhiyun  * @dev: ISHTP device pointer
199*4882a593Smuzhiyun  *
200*4882a593Smuzhiyun  * Read header of 32bit length
201*4882a593Smuzhiyun  *
202*4882a593Smuzhiyun  * Return: Read register value
203*4882a593Smuzhiyun  */
_ishtp_read_hdr(const struct ishtp_device * dev)204*4882a593Smuzhiyun static uint32_t _ishtp_read_hdr(const struct ishtp_device *dev)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun 	return ish_reg_read(dev, IPC_REG_ISH2HOST_MSG);
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun /**
210*4882a593Smuzhiyun  * _ishtp_read - Read message
211*4882a593Smuzhiyun  * @dev: ISHTP device pointer
212*4882a593Smuzhiyun  * @buffer: message buffer
213*4882a593Smuzhiyun  * @buffer_length: length of message buffer
214*4882a593Smuzhiyun  *
215*4882a593Smuzhiyun  * Read message from FW
216*4882a593Smuzhiyun  *
217*4882a593Smuzhiyun  * Return: Always 0
218*4882a593Smuzhiyun  */
_ishtp_read(struct ishtp_device * dev,unsigned char * buffer,unsigned long buffer_length)219*4882a593Smuzhiyun static int _ishtp_read(struct ishtp_device *dev, unsigned char *buffer,
220*4882a593Smuzhiyun 	unsigned long buffer_length)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	uint32_t	i;
223*4882a593Smuzhiyun 	uint32_t	*r_buf = (uint32_t *)buffer;
224*4882a593Smuzhiyun 	uint32_t	msg_offs;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	msg_offs = IPC_REG_ISH2HOST_MSG + sizeof(struct ishtp_msg_hdr);
227*4882a593Smuzhiyun 	for (i = 0; i < buffer_length; i += sizeof(uint32_t))
228*4882a593Smuzhiyun 		*r_buf++ = ish_reg_read(dev, msg_offs + i);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	return 0;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun /**
234*4882a593Smuzhiyun  * write_ipc_from_queue() - try to write ipc msg from Tx queue to device
235*4882a593Smuzhiyun  * @dev: ishtp device pointer
236*4882a593Smuzhiyun  *
237*4882a593Smuzhiyun  * Check if DRBL is cleared. if it is - write the first IPC msg,  then call
238*4882a593Smuzhiyun  * the callback function (unless it's NULL)
239*4882a593Smuzhiyun  *
240*4882a593Smuzhiyun  * Return: 0 for success else failure code
241*4882a593Smuzhiyun  */
write_ipc_from_queue(struct ishtp_device * dev)242*4882a593Smuzhiyun static int write_ipc_from_queue(struct ishtp_device *dev)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun 	struct wr_msg_ctl_info	*ipc_link;
245*4882a593Smuzhiyun 	unsigned long	length;
246*4882a593Smuzhiyun 	unsigned long	rem;
247*4882a593Smuzhiyun 	unsigned long	flags;
248*4882a593Smuzhiyun 	uint32_t	doorbell_val;
249*4882a593Smuzhiyun 	uint32_t	*r_buf;
250*4882a593Smuzhiyun 	uint32_t	reg_addr;
251*4882a593Smuzhiyun 	int	i;
252*4882a593Smuzhiyun 	void	(*ipc_send_compl)(void *);
253*4882a593Smuzhiyun 	void	*ipc_send_compl_prm;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	if (dev->dev_state == ISHTP_DEV_DISABLED)
256*4882a593Smuzhiyun 		return -EINVAL;
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
259*4882a593Smuzhiyun 	if (!ish_is_input_ready(dev)) {
260*4882a593Smuzhiyun 		spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
261*4882a593Smuzhiyun 		return -EBUSY;
262*4882a593Smuzhiyun 	}
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	/*
265*4882a593Smuzhiyun 	 * if tx send list is empty - return 0;
266*4882a593Smuzhiyun 	 * may happen, as RX_COMPLETE handler doesn't check list emptiness.
267*4882a593Smuzhiyun 	 */
268*4882a593Smuzhiyun 	if (list_empty(&dev->wr_processing_list)) {
269*4882a593Smuzhiyun 		spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
270*4882a593Smuzhiyun 		return	0;
271*4882a593Smuzhiyun 	}
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	ipc_link = list_first_entry(&dev->wr_processing_list,
274*4882a593Smuzhiyun 				    struct wr_msg_ctl_info, link);
275*4882a593Smuzhiyun 	/* first 4 bytes of the data is the doorbell value (IPC header) */
276*4882a593Smuzhiyun 	length = ipc_link->length - sizeof(uint32_t);
277*4882a593Smuzhiyun 	doorbell_val = *(uint32_t *)ipc_link->inline_data;
278*4882a593Smuzhiyun 	r_buf = (uint32_t *)(ipc_link->inline_data + sizeof(uint32_t));
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	/* If sending MNG_SYNC_FW_CLOCK, update clock again */
281*4882a593Smuzhiyun 	if (IPC_HEADER_GET_PROTOCOL(doorbell_val) == IPC_PROTOCOL_MNG &&
282*4882a593Smuzhiyun 		IPC_HEADER_GET_MNG_CMD(doorbell_val) == MNG_SYNC_FW_CLOCK) {
283*4882a593Smuzhiyun 		uint64_t usec_system, usec_utc;
284*4882a593Smuzhiyun 		struct ipc_time_update_msg time_update;
285*4882a593Smuzhiyun 		struct time_sync_format ts_format;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 		usec_system = ktime_to_us(ktime_get_boottime());
288*4882a593Smuzhiyun 		usec_utc = ktime_to_us(ktime_get_real());
289*4882a593Smuzhiyun 		ts_format.ts1_source = HOST_SYSTEM_TIME_USEC;
290*4882a593Smuzhiyun 		ts_format.ts2_source = HOST_UTC_TIME_USEC;
291*4882a593Smuzhiyun 		ts_format.reserved = 0;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 		time_update.primary_host_time = usec_system;
294*4882a593Smuzhiyun 		time_update.secondary_host_time = usec_utc;
295*4882a593Smuzhiyun 		time_update.sync_info = ts_format;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 		memcpy(r_buf, &time_update,
298*4882a593Smuzhiyun 		       sizeof(struct ipc_time_update_msg));
299*4882a593Smuzhiyun 	}
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	for (i = 0, reg_addr = IPC_REG_HOST2ISH_MSG; i < length >> 2; i++,
302*4882a593Smuzhiyun 			reg_addr += 4)
303*4882a593Smuzhiyun 		ish_reg_write(dev, reg_addr, r_buf[i]);
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	rem = length & 0x3;
306*4882a593Smuzhiyun 	if (rem > 0) {
307*4882a593Smuzhiyun 		uint32_t reg = 0;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 		memcpy(&reg, &r_buf[length >> 2], rem);
310*4882a593Smuzhiyun 		ish_reg_write(dev, reg_addr, reg);
311*4882a593Smuzhiyun 	}
312*4882a593Smuzhiyun 	ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	/* Flush writes to msg registers and doorbell */
315*4882a593Smuzhiyun 	ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	/* Update IPC counters */
318*4882a593Smuzhiyun 	++dev->ipc_tx_cnt;
319*4882a593Smuzhiyun 	dev->ipc_tx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val);
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	ipc_send_compl = ipc_link->ipc_send_compl;
322*4882a593Smuzhiyun 	ipc_send_compl_prm = ipc_link->ipc_send_compl_prm;
323*4882a593Smuzhiyun 	list_del_init(&ipc_link->link);
324*4882a593Smuzhiyun 	list_add(&ipc_link->link, &dev->wr_free_list);
325*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	/*
328*4882a593Smuzhiyun 	 * callback will be called out of spinlock,
329*4882a593Smuzhiyun 	 * after ipc_link returned to free list
330*4882a593Smuzhiyun 	 */
331*4882a593Smuzhiyun 	if (ipc_send_compl)
332*4882a593Smuzhiyun 		ipc_send_compl(ipc_send_compl_prm);
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	return 0;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun /**
338*4882a593Smuzhiyun  * write_ipc_to_queue() - write ipc msg to Tx queue
339*4882a593Smuzhiyun  * @dev: ishtp device instance
340*4882a593Smuzhiyun  * @ipc_send_compl: Send complete callback
341*4882a593Smuzhiyun  * @ipc_send_compl_prm:	Parameter to send in complete callback
342*4882a593Smuzhiyun  * @msg: Pointer to message
343*4882a593Smuzhiyun  * @length: Length of message
344*4882a593Smuzhiyun  *
345*4882a593Smuzhiyun  * Recived msg with IPC (and upper protocol) header  and add it to the device
346*4882a593Smuzhiyun  *  Tx-to-write list then try to send the first IPC waiting msg
347*4882a593Smuzhiyun  *  (if DRBL is cleared)
348*4882a593Smuzhiyun  * This function returns negative value for failure (means free list
349*4882a593Smuzhiyun  *  is empty, or msg too long) and 0 for success.
350*4882a593Smuzhiyun  *
351*4882a593Smuzhiyun  * Return: 0 for success else failure code
352*4882a593Smuzhiyun  */
write_ipc_to_queue(struct ishtp_device * dev,void (* ipc_send_compl)(void *),void * ipc_send_compl_prm,unsigned char * msg,int length)353*4882a593Smuzhiyun static int write_ipc_to_queue(struct ishtp_device *dev,
354*4882a593Smuzhiyun 	void (*ipc_send_compl)(void *), void *ipc_send_compl_prm,
355*4882a593Smuzhiyun 	unsigned char *msg, int length)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun 	struct wr_msg_ctl_info *ipc_link;
358*4882a593Smuzhiyun 	unsigned long flags;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	if (length > IPC_FULL_MSG_SIZE)
361*4882a593Smuzhiyun 		return -EMSGSIZE;
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
364*4882a593Smuzhiyun 	if (list_empty(&dev->wr_free_list)) {
365*4882a593Smuzhiyun 		spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
366*4882a593Smuzhiyun 		return -ENOMEM;
367*4882a593Smuzhiyun 	}
368*4882a593Smuzhiyun 	ipc_link = list_first_entry(&dev->wr_free_list,
369*4882a593Smuzhiyun 				    struct wr_msg_ctl_info, link);
370*4882a593Smuzhiyun 	list_del_init(&ipc_link->link);
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	ipc_link->ipc_send_compl = ipc_send_compl;
373*4882a593Smuzhiyun 	ipc_link->ipc_send_compl_prm = ipc_send_compl_prm;
374*4882a593Smuzhiyun 	ipc_link->length = length;
375*4882a593Smuzhiyun 	memcpy(ipc_link->inline_data, msg, length);
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	list_add_tail(&ipc_link->link, &dev->wr_processing_list);
378*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	write_ipc_from_queue(dev);
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	return 0;
383*4882a593Smuzhiyun }
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun /**
386*4882a593Smuzhiyun  * ipc_send_mng_msg() - Send management message
387*4882a593Smuzhiyun  * @dev: ishtp device instance
388*4882a593Smuzhiyun  * @msg_code: Message code
389*4882a593Smuzhiyun  * @msg: Pointer to message
390*4882a593Smuzhiyun  * @size: Length of message
391*4882a593Smuzhiyun  *
392*4882a593Smuzhiyun  * Send management message to FW
393*4882a593Smuzhiyun  *
394*4882a593Smuzhiyun  * Return: 0 for success else failure code
395*4882a593Smuzhiyun  */
ipc_send_mng_msg(struct ishtp_device * dev,uint32_t msg_code,void * msg,size_t size)396*4882a593Smuzhiyun static int ipc_send_mng_msg(struct ishtp_device *dev, uint32_t msg_code,
397*4882a593Smuzhiyun 	void *msg, size_t size)
398*4882a593Smuzhiyun {
399*4882a593Smuzhiyun 	unsigned char	ipc_msg[IPC_FULL_MSG_SIZE];
400*4882a593Smuzhiyun 	uint32_t	drbl_val = IPC_BUILD_MNG_MSG(msg_code, size);
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	memcpy(ipc_msg, &drbl_val, sizeof(uint32_t));
403*4882a593Smuzhiyun 	memcpy(ipc_msg + sizeof(uint32_t), msg, size);
404*4882a593Smuzhiyun 	return	write_ipc_to_queue(dev, NULL, NULL, ipc_msg,
405*4882a593Smuzhiyun 		sizeof(uint32_t) + size);
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun #define WAIT_FOR_FW_RDY			0x1
409*4882a593Smuzhiyun #define WAIT_FOR_INPUT_RDY		0x2
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun /**
412*4882a593Smuzhiyun  * timed_wait_for_timeout() - wait special event with timeout
413*4882a593Smuzhiyun  * @dev: ISHTP device pointer
414*4882a593Smuzhiyun  * @condition: indicate the condition for waiting
415*4882a593Smuzhiyun  * @timeinc: time slice for every wait cycle, in ms
416*4882a593Smuzhiyun  * @timeout: time in ms for timeout
417*4882a593Smuzhiyun  *
418*4882a593Smuzhiyun  * This function will check special event to be ready in a loop, the loop
419*4882a593Smuzhiyun  * period is specificd in timeinc. Wait timeout will causes failure.
420*4882a593Smuzhiyun  *
421*4882a593Smuzhiyun  * Return: 0 for success else failure code
422*4882a593Smuzhiyun  */
timed_wait_for_timeout(struct ishtp_device * dev,int condition,unsigned int timeinc,unsigned int timeout)423*4882a593Smuzhiyun static int timed_wait_for_timeout(struct ishtp_device *dev, int condition,
424*4882a593Smuzhiyun 				unsigned int timeinc, unsigned int timeout)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun 	bool complete = false;
427*4882a593Smuzhiyun 	int ret;
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	do {
430*4882a593Smuzhiyun 		if (condition == WAIT_FOR_FW_RDY) {
431*4882a593Smuzhiyun 			complete = ishtp_fw_is_ready(dev);
432*4882a593Smuzhiyun 		} else if (condition == WAIT_FOR_INPUT_RDY) {
433*4882a593Smuzhiyun 			complete = ish_is_input_ready(dev);
434*4882a593Smuzhiyun 		} else {
435*4882a593Smuzhiyun 			ret = -EINVAL;
436*4882a593Smuzhiyun 			goto out;
437*4882a593Smuzhiyun 		}
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 		if (!complete) {
440*4882a593Smuzhiyun 			unsigned long left_time;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 			left_time = msleep_interruptible(timeinc);
443*4882a593Smuzhiyun 			timeout -= (timeinc - left_time);
444*4882a593Smuzhiyun 		}
445*4882a593Smuzhiyun 	} while (!complete && timeout > 0);
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	if (complete)
448*4882a593Smuzhiyun 		ret = 0;
449*4882a593Smuzhiyun 	else
450*4882a593Smuzhiyun 		ret = -EBUSY;
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun out:
453*4882a593Smuzhiyun 	return ret;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun #define TIME_SLICE_FOR_FW_RDY_MS		100
457*4882a593Smuzhiyun #define TIME_SLICE_FOR_INPUT_RDY_MS		100
458*4882a593Smuzhiyun #define TIMEOUT_FOR_FW_RDY_MS			2000
459*4882a593Smuzhiyun #define TIMEOUT_FOR_INPUT_RDY_MS		2000
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun /**
462*4882a593Smuzhiyun  * ish_fw_reset_handler() - FW reset handler
463*4882a593Smuzhiyun  * @dev: ishtp device pointer
464*4882a593Smuzhiyun  *
465*4882a593Smuzhiyun  * Handle FW reset
466*4882a593Smuzhiyun  *
467*4882a593Smuzhiyun  * Return: 0 for success else failure code
468*4882a593Smuzhiyun  */
ish_fw_reset_handler(struct ishtp_device * dev)469*4882a593Smuzhiyun static int ish_fw_reset_handler(struct ishtp_device *dev)
470*4882a593Smuzhiyun {
471*4882a593Smuzhiyun 	uint32_t	reset_id;
472*4882a593Smuzhiyun 	unsigned long	flags;
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	/* Read reset ID */
475*4882a593Smuzhiyun 	reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF;
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	/* Clear IPC output queue */
478*4882a593Smuzhiyun 	spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
479*4882a593Smuzhiyun 	list_splice_init(&dev->wr_processing_list, &dev->wr_free_list);
480*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	/* ISHTP notification in IPC_RESET */
483*4882a593Smuzhiyun 	ishtp_reset_handler(dev);
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	if (!ish_is_input_ready(dev))
486*4882a593Smuzhiyun 		timed_wait_for_timeout(dev, WAIT_FOR_INPUT_RDY,
487*4882a593Smuzhiyun 			TIME_SLICE_FOR_INPUT_RDY_MS, TIMEOUT_FOR_INPUT_RDY_MS);
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	/* ISH FW is dead */
490*4882a593Smuzhiyun 	if (!ish_is_input_ready(dev))
491*4882a593Smuzhiyun 		return	-EPIPE;
492*4882a593Smuzhiyun 	/*
493*4882a593Smuzhiyun 	 * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending
494*4882a593Smuzhiyun 	 * RESET_NOTIFY_ACK - FW will be checking for it
495*4882a593Smuzhiyun 	 */
496*4882a593Smuzhiyun 	ish_set_host_rdy(dev);
497*4882a593Smuzhiyun 	/* Send RESET_NOTIFY_ACK (with reset_id) */
498*4882a593Smuzhiyun 	ipc_send_mng_msg(dev, MNG_RESET_NOTIFY_ACK, &reset_id,
499*4882a593Smuzhiyun 			 sizeof(uint32_t));
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	/* Wait for ISH FW'es ILUP and ISHTP_READY */
502*4882a593Smuzhiyun 	timed_wait_for_timeout(dev, WAIT_FOR_FW_RDY,
503*4882a593Smuzhiyun 			TIME_SLICE_FOR_FW_RDY_MS, TIMEOUT_FOR_FW_RDY_MS);
504*4882a593Smuzhiyun 	if (!ishtp_fw_is_ready(dev)) {
505*4882a593Smuzhiyun 		/* ISH FW is dead */
506*4882a593Smuzhiyun 		uint32_t	ish_status;
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 		ish_status = _ish_read_fw_sts_reg(dev);
509*4882a593Smuzhiyun 		dev_err(dev->devc,
510*4882a593Smuzhiyun 			"[ishtp-ish]: completed reset, ISH is dead (FWSTS = %08X)\n",
511*4882a593Smuzhiyun 			ish_status);
512*4882a593Smuzhiyun 		return -ENODEV;
513*4882a593Smuzhiyun 	}
514*4882a593Smuzhiyun 	return	0;
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun #define TIMEOUT_FOR_HW_RDY_MS			300
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun /**
520*4882a593Smuzhiyun  * ish_fw_reset_work_fn() - FW reset worker function
521*4882a593Smuzhiyun  * @unused: not used
522*4882a593Smuzhiyun  *
523*4882a593Smuzhiyun  * Call ish_fw_reset_handler to complete FW reset
524*4882a593Smuzhiyun  */
fw_reset_work_fn(struct work_struct * unused)525*4882a593Smuzhiyun static void fw_reset_work_fn(struct work_struct *unused)
526*4882a593Smuzhiyun {
527*4882a593Smuzhiyun 	int	rv;
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	rv = ish_fw_reset_handler(ishtp_dev);
530*4882a593Smuzhiyun 	if (!rv) {
531*4882a593Smuzhiyun 		/* ISH is ILUP & ISHTP-ready. Restart ISHTP */
532*4882a593Smuzhiyun 		msleep_interruptible(TIMEOUT_FOR_HW_RDY_MS);
533*4882a593Smuzhiyun 		ishtp_dev->recvd_hw_ready = 1;
534*4882a593Smuzhiyun 		wake_up_interruptible(&ishtp_dev->wait_hw_ready);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 		/* ISHTP notification in IPC_RESET sequence completion */
537*4882a593Smuzhiyun 		ishtp_reset_compl_handler(ishtp_dev);
538*4882a593Smuzhiyun 	} else
539*4882a593Smuzhiyun 		dev_err(ishtp_dev->devc, "[ishtp-ish]: FW reset failed (%d)\n",
540*4882a593Smuzhiyun 			rv);
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun /**
544*4882a593Smuzhiyun  * _ish_sync_fw_clock() -Sync FW clock with the OS clock
545*4882a593Smuzhiyun  * @dev: ishtp device pointer
546*4882a593Smuzhiyun  *
547*4882a593Smuzhiyun  * Sync FW and OS time
548*4882a593Smuzhiyun  */
_ish_sync_fw_clock(struct ishtp_device * dev)549*4882a593Smuzhiyun static void _ish_sync_fw_clock(struct ishtp_device *dev)
550*4882a593Smuzhiyun {
551*4882a593Smuzhiyun 	static unsigned long	prev_sync;
552*4882a593Smuzhiyun 	uint64_t	usec;
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	if (prev_sync && jiffies - prev_sync < 20 * HZ)
555*4882a593Smuzhiyun 		return;
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	prev_sync = jiffies;
558*4882a593Smuzhiyun 	usec = ktime_to_us(ktime_get_boottime());
559*4882a593Smuzhiyun 	ipc_send_mng_msg(dev, MNG_SYNC_FW_CLOCK, &usec, sizeof(uint64_t));
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun /**
563*4882a593Smuzhiyun  * recv_ipc() - Receive and process IPC management messages
564*4882a593Smuzhiyun  * @dev: ishtp device instance
565*4882a593Smuzhiyun  * @doorbell_val: doorbell value
566*4882a593Smuzhiyun  *
567*4882a593Smuzhiyun  * This function runs in ISR context.
568*4882a593Smuzhiyun  * NOTE: Any other mng command than reset_notify and reset_notify_ack
569*4882a593Smuzhiyun  * won't wake BH handler
570*4882a593Smuzhiyun  */
recv_ipc(struct ishtp_device * dev,uint32_t doorbell_val)571*4882a593Smuzhiyun static void	recv_ipc(struct ishtp_device *dev, uint32_t doorbell_val)
572*4882a593Smuzhiyun {
573*4882a593Smuzhiyun 	uint32_t	mng_cmd;
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	mng_cmd = IPC_HEADER_GET_MNG_CMD(doorbell_val);
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	switch (mng_cmd) {
578*4882a593Smuzhiyun 	default:
579*4882a593Smuzhiyun 		break;
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	case MNG_RX_CMPL_INDICATION:
582*4882a593Smuzhiyun 		if (dev->suspend_flag) {
583*4882a593Smuzhiyun 			dev->suspend_flag = 0;
584*4882a593Smuzhiyun 			wake_up_interruptible(&dev->suspend_wait);
585*4882a593Smuzhiyun 		}
586*4882a593Smuzhiyun 		if (dev->resume_flag) {
587*4882a593Smuzhiyun 			dev->resume_flag = 0;
588*4882a593Smuzhiyun 			wake_up_interruptible(&dev->resume_wait);
589*4882a593Smuzhiyun 		}
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 		write_ipc_from_queue(dev);
592*4882a593Smuzhiyun 		break;
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	case MNG_RESET_NOTIFY:
595*4882a593Smuzhiyun 		if (!ishtp_dev) {
596*4882a593Smuzhiyun 			ishtp_dev = dev;
597*4882a593Smuzhiyun 			INIT_WORK(&fw_reset_work, fw_reset_work_fn);
598*4882a593Smuzhiyun 		}
599*4882a593Smuzhiyun 		schedule_work(&fw_reset_work);
600*4882a593Smuzhiyun 		break;
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	case MNG_RESET_NOTIFY_ACK:
603*4882a593Smuzhiyun 		dev->recvd_hw_ready = 1;
604*4882a593Smuzhiyun 		wake_up_interruptible(&dev->wait_hw_ready);
605*4882a593Smuzhiyun 		break;
606*4882a593Smuzhiyun 	}
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun /**
610*4882a593Smuzhiyun  * ish_irq_handler() - ISH IRQ handler
611*4882a593Smuzhiyun  * @irq: irq number
612*4882a593Smuzhiyun  * @dev_id: ishtp device pointer
613*4882a593Smuzhiyun  *
614*4882a593Smuzhiyun  * ISH IRQ handler. If interrupt is generated and is for ISH it will process
615*4882a593Smuzhiyun  * the interrupt.
616*4882a593Smuzhiyun  */
ish_irq_handler(int irq,void * dev_id)617*4882a593Smuzhiyun irqreturn_t ish_irq_handler(int irq, void *dev_id)
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun 	struct ishtp_device	*dev = dev_id;
620*4882a593Smuzhiyun 	uint32_t	doorbell_val;
621*4882a593Smuzhiyun 	bool	interrupt_generated;
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 	/* Check that it's interrupt from ISH (may be shared) */
624*4882a593Smuzhiyun 	interrupt_generated = check_generated_interrupt(dev);
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	if (!interrupt_generated)
627*4882a593Smuzhiyun 		return IRQ_NONE;
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	doorbell_val = ish_reg_read(dev, IPC_REG_ISH2HOST_DRBL);
630*4882a593Smuzhiyun 	if (!IPC_IS_BUSY(doorbell_val))
631*4882a593Smuzhiyun 		return IRQ_HANDLED;
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	if (dev->dev_state == ISHTP_DEV_DISABLED)
634*4882a593Smuzhiyun 		return	IRQ_HANDLED;
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	/* Sanity check: IPC dgram length in header */
637*4882a593Smuzhiyun 	if (IPC_HEADER_GET_LENGTH(doorbell_val) > IPC_PAYLOAD_SIZE) {
638*4882a593Smuzhiyun 		dev_err(dev->devc,
639*4882a593Smuzhiyun 			"IPC hdr - bad length: %u; dropped\n",
640*4882a593Smuzhiyun 			(unsigned int)IPC_HEADER_GET_LENGTH(doorbell_val));
641*4882a593Smuzhiyun 		goto	eoi;
642*4882a593Smuzhiyun 	}
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun 	switch (IPC_HEADER_GET_PROTOCOL(doorbell_val)) {
645*4882a593Smuzhiyun 	default:
646*4882a593Smuzhiyun 		break;
647*4882a593Smuzhiyun 	case IPC_PROTOCOL_MNG:
648*4882a593Smuzhiyun 		recv_ipc(dev, doorbell_val);
649*4882a593Smuzhiyun 		break;
650*4882a593Smuzhiyun 	case IPC_PROTOCOL_ISHTP:
651*4882a593Smuzhiyun 		ishtp_recv(dev);
652*4882a593Smuzhiyun 		break;
653*4882a593Smuzhiyun 	}
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun eoi:
656*4882a593Smuzhiyun 	/* Update IPC counters */
657*4882a593Smuzhiyun 	++dev->ipc_rx_cnt;
658*4882a593Smuzhiyun 	dev->ipc_rx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val);
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0);
661*4882a593Smuzhiyun 	/* Flush write to doorbell */
662*4882a593Smuzhiyun 	ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	return	IRQ_HANDLED;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun /**
668*4882a593Smuzhiyun  * ish_disable_dma() - disable dma communication between host and ISHFW
669*4882a593Smuzhiyun  * @dev: ishtp device pointer
670*4882a593Smuzhiyun  *
671*4882a593Smuzhiyun  * Clear the dma enable bit and wait for dma inactive.
672*4882a593Smuzhiyun  *
673*4882a593Smuzhiyun  * Return: 0 for success else error code.
674*4882a593Smuzhiyun  */
ish_disable_dma(struct ishtp_device * dev)675*4882a593Smuzhiyun int ish_disable_dma(struct ishtp_device *dev)
676*4882a593Smuzhiyun {
677*4882a593Smuzhiyun 	unsigned int	dma_delay;
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun 	/* Clear the dma enable bit */
680*4882a593Smuzhiyun 	ish_reg_write(dev, IPC_REG_ISH_RMP2, 0);
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	/* wait for dma inactive */
683*4882a593Smuzhiyun 	for (dma_delay = 0; dma_delay < MAX_DMA_DELAY &&
684*4882a593Smuzhiyun 		_ish_read_fw_sts_reg(dev) & (IPC_ISH_IN_DMA);
685*4882a593Smuzhiyun 		dma_delay += 5)
686*4882a593Smuzhiyun 		mdelay(5);
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	if (dma_delay >= MAX_DMA_DELAY) {
689*4882a593Smuzhiyun 		dev_err(dev->devc,
690*4882a593Smuzhiyun 			"Wait for DMA inactive timeout\n");
691*4882a593Smuzhiyun 		return	-EBUSY;
692*4882a593Smuzhiyun 	}
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	return 0;
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun /**
698*4882a593Smuzhiyun  * ish_wakeup() - wakeup ishfw from waiting-for-host state
699*4882a593Smuzhiyun  * @dev: ishtp device pointer
700*4882a593Smuzhiyun  *
701*4882a593Smuzhiyun  * Set the dma enable bit and send a void message to FW,
702*4882a593Smuzhiyun  * it wil wakeup FW from waiting-for-host state.
703*4882a593Smuzhiyun  */
ish_wakeup(struct ishtp_device * dev)704*4882a593Smuzhiyun static void ish_wakeup(struct ishtp_device *dev)
705*4882a593Smuzhiyun {
706*4882a593Smuzhiyun 	/* Set dma enable bit */
707*4882a593Smuzhiyun 	ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED);
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun 	/*
710*4882a593Smuzhiyun 	 * Send 0 IPC message so that ISH FW wakes up if it was already
711*4882a593Smuzhiyun 	 * asleep.
712*4882a593Smuzhiyun 	 */
713*4882a593Smuzhiyun 	ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT);
714*4882a593Smuzhiyun 
715*4882a593Smuzhiyun 	/* Flush writes to doorbell and REMAP2 */
716*4882a593Smuzhiyun 	ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
717*4882a593Smuzhiyun }
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun /**
720*4882a593Smuzhiyun  * _ish_hw_reset() - HW reset
721*4882a593Smuzhiyun  * @dev: ishtp device pointer
722*4882a593Smuzhiyun  *
723*4882a593Smuzhiyun  * Reset ISH HW to recover if any error
724*4882a593Smuzhiyun  *
725*4882a593Smuzhiyun  * Return: 0 for success else error fault code
726*4882a593Smuzhiyun  */
_ish_hw_reset(struct ishtp_device * dev)727*4882a593Smuzhiyun static int _ish_hw_reset(struct ishtp_device *dev)
728*4882a593Smuzhiyun {
729*4882a593Smuzhiyun 	struct pci_dev *pdev = dev->pdev;
730*4882a593Smuzhiyun 	int	rv;
731*4882a593Smuzhiyun 	uint16_t csr;
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun 	if (!pdev)
734*4882a593Smuzhiyun 		return	-ENODEV;
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	rv = pci_reset_function(pdev);
737*4882a593Smuzhiyun 	if (!rv)
738*4882a593Smuzhiyun 		dev->dev_state = ISHTP_DEV_RESETTING;
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	if (!pdev->pm_cap) {
741*4882a593Smuzhiyun 		dev_err(&pdev->dev, "Can't reset - no PM caps\n");
742*4882a593Smuzhiyun 		return	-EINVAL;
743*4882a593Smuzhiyun 	}
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun 	/* Disable dma communication between FW and host */
746*4882a593Smuzhiyun 	if (ish_disable_dma(dev)) {
747*4882a593Smuzhiyun 		dev_err(&pdev->dev,
748*4882a593Smuzhiyun 			"Can't reset - stuck with DMA in-progress\n");
749*4882a593Smuzhiyun 		return	-EBUSY;
750*4882a593Smuzhiyun 	}
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 	pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &csr);
753*4882a593Smuzhiyun 
754*4882a593Smuzhiyun 	csr &= ~PCI_PM_CTRL_STATE_MASK;
755*4882a593Smuzhiyun 	csr |= PCI_D3hot;
756*4882a593Smuzhiyun 	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr);
757*4882a593Smuzhiyun 
758*4882a593Smuzhiyun 	mdelay(pdev->d3hot_delay);
759*4882a593Smuzhiyun 
760*4882a593Smuzhiyun 	csr &= ~PCI_PM_CTRL_STATE_MASK;
761*4882a593Smuzhiyun 	csr |= PCI_D0;
762*4882a593Smuzhiyun 	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr);
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	/* Now we can enable ISH DMA operation and wakeup ISHFW */
765*4882a593Smuzhiyun 	ish_wakeup(dev);
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 	return	0;
768*4882a593Smuzhiyun }
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun /**
771*4882a593Smuzhiyun  * _ish_ipc_reset() - IPC reset
772*4882a593Smuzhiyun  * @dev: ishtp device pointer
773*4882a593Smuzhiyun  *
774*4882a593Smuzhiyun  * Resets host and fw IPC and upper layers
775*4882a593Smuzhiyun  *
776*4882a593Smuzhiyun  * Return: 0 for success else error fault code
777*4882a593Smuzhiyun  */
_ish_ipc_reset(struct ishtp_device * dev)778*4882a593Smuzhiyun static int _ish_ipc_reset(struct ishtp_device *dev)
779*4882a593Smuzhiyun {
780*4882a593Smuzhiyun 	struct ipc_rst_payload_type ipc_mng_msg;
781*4882a593Smuzhiyun 	int	rv = 0;
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	ipc_mng_msg.reset_id = 1;
784*4882a593Smuzhiyun 	ipc_mng_msg.reserved = 0;
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	set_host_ready(dev);
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun 	/* Clear the incoming doorbell */
789*4882a593Smuzhiyun 	ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0);
790*4882a593Smuzhiyun 	/* Flush write to doorbell */
791*4882a593Smuzhiyun 	ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
792*4882a593Smuzhiyun 
793*4882a593Smuzhiyun 	dev->recvd_hw_ready = 0;
794*4882a593Smuzhiyun 
795*4882a593Smuzhiyun 	/* send message */
796*4882a593Smuzhiyun 	rv = ipc_send_mng_msg(dev, MNG_RESET_NOTIFY, &ipc_mng_msg,
797*4882a593Smuzhiyun 		sizeof(struct ipc_rst_payload_type));
798*4882a593Smuzhiyun 	if (rv) {
799*4882a593Smuzhiyun 		dev_err(dev->devc, "Failed to send IPC MNG_RESET_NOTIFY\n");
800*4882a593Smuzhiyun 		return	rv;
801*4882a593Smuzhiyun 	}
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 	wait_event_interruptible_timeout(dev->wait_hw_ready,
804*4882a593Smuzhiyun 					 dev->recvd_hw_ready, 2 * HZ);
805*4882a593Smuzhiyun 	if (!dev->recvd_hw_ready) {
806*4882a593Smuzhiyun 		dev_err(dev->devc, "Timed out waiting for HW ready\n");
807*4882a593Smuzhiyun 		rv = -ENODEV;
808*4882a593Smuzhiyun 	}
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 	return rv;
811*4882a593Smuzhiyun }
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun /**
814*4882a593Smuzhiyun  * ish_hw_start() -Start ISH HW
815*4882a593Smuzhiyun  * @dev: ishtp device pointer
816*4882a593Smuzhiyun  *
817*4882a593Smuzhiyun  * Set host to ready state and wait for FW reset
818*4882a593Smuzhiyun  *
819*4882a593Smuzhiyun  * Return: 0 for success else error fault code
820*4882a593Smuzhiyun  */
ish_hw_start(struct ishtp_device * dev)821*4882a593Smuzhiyun int ish_hw_start(struct ishtp_device *dev)
822*4882a593Smuzhiyun {
823*4882a593Smuzhiyun 	ish_set_host_rdy(dev);
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	set_host_ready(dev);
826*4882a593Smuzhiyun 
827*4882a593Smuzhiyun 	/* After that we can enable ISH DMA operation and wakeup ISHFW */
828*4882a593Smuzhiyun 	ish_wakeup(dev);
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	/* wait for FW-initiated reset flow */
831*4882a593Smuzhiyun 	if (!dev->recvd_hw_ready)
832*4882a593Smuzhiyun 		wait_event_interruptible_timeout(dev->wait_hw_ready,
833*4882a593Smuzhiyun 						 dev->recvd_hw_ready,
834*4882a593Smuzhiyun 						 10 * HZ);
835*4882a593Smuzhiyun 
836*4882a593Smuzhiyun 	if (!dev->recvd_hw_ready) {
837*4882a593Smuzhiyun 		dev_err(dev->devc,
838*4882a593Smuzhiyun 			"[ishtp-ish]: Timed out waiting for FW-initiated reset\n");
839*4882a593Smuzhiyun 		return	-ENODEV;
840*4882a593Smuzhiyun 	}
841*4882a593Smuzhiyun 
842*4882a593Smuzhiyun 	return 0;
843*4882a593Smuzhiyun }
844*4882a593Smuzhiyun 
845*4882a593Smuzhiyun /**
846*4882a593Smuzhiyun  * ish_ipc_get_header() -Get doorbell value
847*4882a593Smuzhiyun  * @dev: ishtp device pointer
848*4882a593Smuzhiyun  * @length: length of message
849*4882a593Smuzhiyun  * @busy: busy status
850*4882a593Smuzhiyun  *
851*4882a593Smuzhiyun  * Get door bell value from message header
852*4882a593Smuzhiyun  *
853*4882a593Smuzhiyun  * Return: door bell value
854*4882a593Smuzhiyun  */
ish_ipc_get_header(struct ishtp_device * dev,int length,int busy)855*4882a593Smuzhiyun static uint32_t ish_ipc_get_header(struct ishtp_device *dev, int length,
856*4882a593Smuzhiyun 				   int busy)
857*4882a593Smuzhiyun {
858*4882a593Smuzhiyun 	uint32_t drbl_val;
859*4882a593Smuzhiyun 
860*4882a593Smuzhiyun 	drbl_val = IPC_BUILD_HEADER(length, IPC_PROTOCOL_ISHTP, busy);
861*4882a593Smuzhiyun 
862*4882a593Smuzhiyun 	return drbl_val;
863*4882a593Smuzhiyun }
864*4882a593Smuzhiyun 
865*4882a593Smuzhiyun static const struct ishtp_hw_ops ish_hw_ops = {
866*4882a593Smuzhiyun 	.hw_reset = _ish_hw_reset,
867*4882a593Smuzhiyun 	.ipc_reset = _ish_ipc_reset,
868*4882a593Smuzhiyun 	.ipc_get_header = ish_ipc_get_header,
869*4882a593Smuzhiyun 	.ishtp_read = _ishtp_read,
870*4882a593Smuzhiyun 	.write = write_ipc_to_queue,
871*4882a593Smuzhiyun 	.get_fw_status = _ish_read_fw_sts_reg,
872*4882a593Smuzhiyun 	.sync_fw_clock = _ish_sync_fw_clock,
873*4882a593Smuzhiyun 	.ishtp_read_hdr = _ishtp_read_hdr
874*4882a593Smuzhiyun };
875*4882a593Smuzhiyun 
876*4882a593Smuzhiyun /**
877*4882a593Smuzhiyun  * ish_dev_init() -Initialize ISH devoce
878*4882a593Smuzhiyun  * @pdev: PCI device
879*4882a593Smuzhiyun  *
880*4882a593Smuzhiyun  * Allocate ISHTP device and initialize IPC processing
881*4882a593Smuzhiyun  *
882*4882a593Smuzhiyun  * Return: ISHTP device instance on success else NULL
883*4882a593Smuzhiyun  */
ish_dev_init(struct pci_dev * pdev)884*4882a593Smuzhiyun struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
885*4882a593Smuzhiyun {
886*4882a593Smuzhiyun 	struct ishtp_device *dev;
887*4882a593Smuzhiyun 	int	i;
888*4882a593Smuzhiyun 
889*4882a593Smuzhiyun 	dev = devm_kzalloc(&pdev->dev,
890*4882a593Smuzhiyun 			   sizeof(struct ishtp_device) + sizeof(struct ish_hw),
891*4882a593Smuzhiyun 			   GFP_KERNEL);
892*4882a593Smuzhiyun 	if (!dev)
893*4882a593Smuzhiyun 		return NULL;
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun 	ishtp_device_init(dev);
896*4882a593Smuzhiyun 
897*4882a593Smuzhiyun 	init_waitqueue_head(&dev->wait_hw_ready);
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun 	spin_lock_init(&dev->wr_processing_spinlock);
900*4882a593Smuzhiyun 
901*4882a593Smuzhiyun 	/* Init IPC processing and free lists */
902*4882a593Smuzhiyun 	INIT_LIST_HEAD(&dev->wr_processing_list);
903*4882a593Smuzhiyun 	INIT_LIST_HEAD(&dev->wr_free_list);
904*4882a593Smuzhiyun 	for (i = 0; i < IPC_TX_FIFO_SIZE; i++) {
905*4882a593Smuzhiyun 		struct wr_msg_ctl_info	*tx_buf;
906*4882a593Smuzhiyun 
907*4882a593Smuzhiyun 		tx_buf = devm_kzalloc(&pdev->dev,
908*4882a593Smuzhiyun 				      sizeof(struct wr_msg_ctl_info),
909*4882a593Smuzhiyun 				      GFP_KERNEL);
910*4882a593Smuzhiyun 		if (!tx_buf) {
911*4882a593Smuzhiyun 			/*
912*4882a593Smuzhiyun 			 * IPC buffers may be limited or not available
913*4882a593Smuzhiyun 			 * at all - although this shouldn't happen
914*4882a593Smuzhiyun 			 */
915*4882a593Smuzhiyun 			dev_err(dev->devc,
916*4882a593Smuzhiyun 				"[ishtp-ish]: failure in Tx FIFO allocations (%d)\n",
917*4882a593Smuzhiyun 				i);
918*4882a593Smuzhiyun 			break;
919*4882a593Smuzhiyun 		}
920*4882a593Smuzhiyun 		list_add_tail(&tx_buf->link, &dev->wr_free_list);
921*4882a593Smuzhiyun 	}
922*4882a593Smuzhiyun 
923*4882a593Smuzhiyun 	dev->ops = &ish_hw_ops;
924*4882a593Smuzhiyun 	dev->devc = &pdev->dev;
925*4882a593Smuzhiyun 	dev->mtu = IPC_PAYLOAD_SIZE - sizeof(struct ishtp_msg_hdr);
926*4882a593Smuzhiyun 	return dev;
927*4882a593Smuzhiyun }
928*4882a593Smuzhiyun 
929*4882a593Smuzhiyun /**
930*4882a593Smuzhiyun  * ish_device_disable() - Disable ISH device
931*4882a593Smuzhiyun  * @dev: ISHTP device pointer
932*4882a593Smuzhiyun  *
933*4882a593Smuzhiyun  * Disable ISH by clearing host ready to inform firmware.
934*4882a593Smuzhiyun  */
ish_device_disable(struct ishtp_device * dev)935*4882a593Smuzhiyun void	ish_device_disable(struct ishtp_device *dev)
936*4882a593Smuzhiyun {
937*4882a593Smuzhiyun 	struct pci_dev *pdev = dev->pdev;
938*4882a593Smuzhiyun 
939*4882a593Smuzhiyun 	if (!pdev)
940*4882a593Smuzhiyun 		return;
941*4882a593Smuzhiyun 
942*4882a593Smuzhiyun 	/* Disable dma communication between FW and host */
943*4882a593Smuzhiyun 	if (ish_disable_dma(dev)) {
944*4882a593Smuzhiyun 		dev_err(&pdev->dev,
945*4882a593Smuzhiyun 			"Can't reset - stuck with DMA in-progress\n");
946*4882a593Smuzhiyun 		return;
947*4882a593Smuzhiyun 	}
948*4882a593Smuzhiyun 
949*4882a593Smuzhiyun 	/* Put ISH to D3hot state for power saving */
950*4882a593Smuzhiyun 	pci_set_power_state(pdev, PCI_D3hot);
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun 	dev->dev_state = ISHTP_DEV_DISABLED;
953*4882a593Smuzhiyun 	ish_clr_host_rdy(dev);
954*4882a593Smuzhiyun }
955