1/* uisqueue.c
2 *
3 * Copyright (C) 2010 - 2013 UNISYS CORPORATION
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14 * NON INFRINGEMENT.  See the GNU General Public License for more
15 * details.
16 */
17
18/* @ALL_INSPECTED */
19#include <linux/kernel.h>
20#include <linux/module.h>
21
22#include "uisutils.h"
23
24/* this is shorter than using __FILE__ (full path name) in
25 * debug/info/error messages */
26#define CURRENT_FILE_PC UISLIB_PC_uisqueue_c
27#define __MYFILE__ "uisqueue.c"
28
29#define CHECK_CACHE_ALIGN 0
30
31/*****************************************************/
32/* Exported functions                                */
33/*****************************************************/
34
35/*
36 * Routine Description:
37 * Tries to insert the prebuilt signal pointed to by pSignal into the nth
38 * Queue of the Channel pointed to by pChannel
39 *
40 * Parameters:
41 * pChannel: (IN) points to the IO Channel
42 * Queue: (IN) nth Queue of the IO Channel
43 * pSignal: (IN) pointer to the signal
44 *
45 * Assumptions:
46 * - pChannel, Queue and pSignal are valid.
47 * - If insertion fails due to a full queue, the caller will determine the
48 * retry policy (e.g. wait & try again, report an error, etc.).
49 *
50 * Return value:
51 * 1 if the insertion succeeds, 0 if the queue was full.
52 */
53unsigned char spar_signal_insert(struct channel_header __iomem *ch, u32 queue,
54				 void *sig)
55{
56	void __iomem *psignal;
57	unsigned int head, tail, nof;
58
59	struct signal_queue_header __iomem *pqhdr =
60	    (struct signal_queue_header __iomem *)
61		((char __iomem *)ch + readq(&ch->ch_space_offset))
62		+ queue;
63
64	/* capture current head and tail */
65	head = readl(&pqhdr->head);
66	tail = readl(&pqhdr->tail);
67
68	/* queue is full if (head + 1) % n equals tail */
69	if (((head + 1) % readl(&pqhdr->max_slots)) == tail) {
70		nof = readq(&pqhdr->num_overflows) + 1;
71		writeq(nof, &pqhdr->num_overflows);
72		return 0;
73	}
74
75	/* increment the head index */
76	head = (head + 1) % readl(&pqhdr->max_slots);
77
78	/* copy signal to the head location from the area pointed to
79	 * by pSignal
80	 */
81	psignal = (char __iomem *)pqhdr + readq(&pqhdr->sig_base_offset) +
82		(head * readl(&pqhdr->signal_size));
83	memcpy_toio(psignal, sig, readl(&pqhdr->signal_size));
84
85	mb(); /* channel synch */
86	writel(head, &pqhdr->head);
87
88	writeq(readq(&pqhdr->num_sent) + 1, &pqhdr->num_sent);
89	return 1;
90}
91EXPORT_SYMBOL_GPL(spar_signal_insert);
92
93/*
94 * Routine Description:
95 * Removes one signal from Channel pChannel's nth Queue at the
96 * time of the call and copies it into the memory pointed to by
97 * pSignal.
98 *
99 * Parameters:
100 * pChannel: (IN) points to the IO Channel
101 * Queue: (IN) nth Queue of the IO Channel
102 * pSignal: (IN) pointer to where the signals are to be copied
103 *
104 * Assumptions:
105 * - pChannel and Queue are valid.
106 * - pSignal points to a memory area large enough to hold queue's SignalSize
107 *
108 * Return value:
109 * 1 if the removal succeeds, 0 if the queue was empty.
110 */
111unsigned char
112spar_signal_remove(struct channel_header __iomem *ch, u32 queue, void *sig)
113{
114	void __iomem *psource;
115	unsigned int head, tail;
116	struct signal_queue_header __iomem *pqhdr =
117	    (struct signal_queue_header __iomem *)((char __iomem *)ch +
118				    readq(&ch->ch_space_offset)) + queue;
119
120	/* capture current head and tail */
121	head = readl(&pqhdr->head);
122	tail = readl(&pqhdr->tail);
123
124	/* queue is empty if the head index equals the tail index */
125	if (head == tail) {
126		writeq(readq(&pqhdr->num_empty) + 1, &pqhdr->num_empty);
127		return 0;
128	}
129
130	/* advance past the 'empty' front slot */
131	tail = (tail + 1) % readl(&pqhdr->max_slots);
132
133	/* copy signal from tail location to the area pointed to by pSignal */
134	psource = (char __iomem *)pqhdr + readq(&pqhdr->sig_base_offset) +
135		(tail * readl(&pqhdr->signal_size));
136	memcpy_fromio(sig, psource, readl(&pqhdr->signal_size));
137
138	mb(); /* channel synch */
139	writel(tail, &pqhdr->tail);
140
141	writeq(readq(&pqhdr->num_received) + 1,
142	       &pqhdr->num_received);
143	return 1;
144}
145EXPORT_SYMBOL_GPL(spar_signal_remove);
146
147/*
148 * Routine Description:
149 * Removes all signals present in Channel pChannel's nth Queue at the
150 * time of the call and copies them into the memory pointed to by
151 * pSignal.  Returns the # of signals copied as the value of the routine.
152 *
153 * Parameters:
154 * pChannel: (IN) points to the IO Channel
155 * Queue: (IN) nth Queue of the IO Channel
156 * pSignal: (IN) pointer to where the signals are to be copied
157 *
158 * Assumptions:
159 * - pChannel and Queue are valid.
160 * - pSignal points to a memory area large enough to hold Queue's MaxSignals
161 * # of signals, each of which is Queue's SignalSize.
162 *
163 * Return value:
164 * # of signals copied.
165 */
166unsigned int spar_signal_remove_all(struct channel_header *ch, u32 queue,
167				    void *sig)
168{
169	void *psource;
170	unsigned int head, tail, count = 0;
171	struct signal_queue_header *pqhdr =
172	    (struct signal_queue_header *)((char *)ch +
173				    ch->ch_space_offset) + queue;
174
175	/* capture current head and tail */
176	head = pqhdr->head;
177	tail = pqhdr->tail;
178
179	/* queue is empty if the head index equals the tail index */
180	if (head == tail)
181		return 0;
182
183	while (head != tail) {
184		/* advance past the 'empty' front slot */
185		tail = (tail + 1) % pqhdr->max_slots;
186
187		/* copy signal from tail location to the area pointed
188		 * to by pSignal
189		 */
190		psource =
191		    (char *)pqhdr + pqhdr->sig_base_offset +
192		    (tail * pqhdr->signal_size);
193		memcpy((char *)sig + (pqhdr->signal_size * count),
194		       psource, pqhdr->signal_size);
195
196		mb(); /* channel synch */
197		pqhdr->tail = tail;
198
199		count++;
200		pqhdr->num_received++;
201	}
202
203	return count;
204}
205
206/*
207 * Routine Description:
208 * Determine whether a signal queue is empty.
209 *
210 * Parameters:
211 * pChannel: (IN) points to the IO Channel
212 * Queue: (IN) nth Queue of the IO Channel
213 *
214 * Return value:
215 * 1 if the signal queue is empty, 0 otherwise.
216 */
217unsigned char spar_signalqueue_empty(struct channel_header __iomem *ch,
218				     u32 queue)
219{
220	struct signal_queue_header __iomem *pqhdr =
221	    (struct signal_queue_header __iomem *)((char __iomem *)ch +
222				    readq(&ch->ch_space_offset)) + queue;
223	return readl(&pqhdr->head) == readl(&pqhdr->tail);
224}
225EXPORT_SYMBOL_GPL(spar_signalqueue_empty);
226
227unsigned long long
228uisqueue_interlocked_or(unsigned long long __iomem *tgt,
229			unsigned long long set)
230{
231	unsigned long long i;
232	unsigned long long j;
233
234	j = readq(tgt);
235	do {
236		i = j;
237		j = cmpxchg((__force unsigned long long *)tgt, i, i | set);
238
239	} while (i != j);
240
241	return j;
242}
243EXPORT_SYMBOL_GPL(uisqueue_interlocked_or);
244
245unsigned long long
246uisqueue_interlocked_and(unsigned long long __iomem *tgt,
247			 unsigned long long set)
248{
249	unsigned long long i;
250	unsigned long long j;
251
252	j = readq(tgt);
253	do {
254		i = j;
255		j = cmpxchg((__force unsigned long long *)tgt, i, i & set);
256
257	} while (i != j);
258
259	return j;
260}
261EXPORT_SYMBOL_GPL(uisqueue_interlocked_and);
262
263static u8
264do_locked_client_insert(struct uisqueue_info *queueinfo,
265			unsigned int whichqueue,
266			void *signal,
267			spinlock_t *lock,
268			u8 *channel_id)
269{
270	unsigned long flags;
271	u8 rc = 0;
272
273	spin_lock_irqsave(lock, flags);
274	if (!spar_channel_client_acquire_os(queueinfo->chan, channel_id))
275		goto unlock;
276	if (spar_signal_insert(queueinfo->chan, whichqueue, signal)) {
277		queueinfo->packets_sent++;
278		rc = 1;
279	}
280	spar_channel_client_release_os(queueinfo->chan, channel_id);
281unlock:
282	spin_unlock_irqrestore((spinlock_t *)lock, flags);
283	return rc;
284}
285
286int
287uisqueue_put_cmdrsp_with_lock_client(struct uisqueue_info *queueinfo,
288				     struct uiscmdrsp *cmdrsp,
289				     unsigned int whichqueue,
290				     void *insertlock,
291				     unsigned char issue_irq_if_empty,
292				     u64 irq_handle,
293				     char oktowait, u8 *channel_id)
294{
295	while (!do_locked_client_insert(queueinfo, whichqueue, cmdrsp,
296					(spinlock_t *)insertlock,
297					channel_id)) {
298		if (oktowait != OK_TO_WAIT)
299			return 0;	/* failed to queue */
300
301		/* try again */
302		set_current_state(TASK_INTERRUPTIBLE);
303		schedule_timeout(msecs_to_jiffies(10));
304	}
305	return 1;
306}
307EXPORT_SYMBOL_GPL(uisqueue_put_cmdrsp_with_lock_client);
308
309/* uisqueue_get_cmdrsp gets the cmdrsp entry at the head of the queue
310 * returns NULL if queue is empty */
311int
312uisqueue_get_cmdrsp(struct uisqueue_info *queueinfo,
313		    void *cmdrsp, unsigned int whichqueue)
314{
315	if (!spar_signal_remove(queueinfo->chan, whichqueue, cmdrsp))
316		return 0;
317
318	queueinfo->packets_received++;
319
320	return 1;		/* Success */
321}
322EXPORT_SYMBOL_GPL(uisqueue_get_cmdrsp);
323