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  */
spar_signal_insert(struct channel_header __iomem * ch,u32 queue,void * sig)53 unsigned 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 }
91 EXPORT_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  */
111 unsigned char
spar_signal_remove(struct channel_header __iomem * ch,u32 queue,void * sig)112 spar_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 }
145 EXPORT_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  */
spar_signal_remove_all(struct channel_header * ch,u32 queue,void * sig)166 unsigned 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  */
spar_signalqueue_empty(struct channel_header __iomem * ch,u32 queue)217 unsigned 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 }
225 EXPORT_SYMBOL_GPL(spar_signalqueue_empty);
226 
227 unsigned long long
uisqueue_interlocked_or(unsigned long long __iomem * tgt,unsigned long long set)228 uisqueue_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 }
243 EXPORT_SYMBOL_GPL(uisqueue_interlocked_or);
244 
245 unsigned long long
uisqueue_interlocked_and(unsigned long long __iomem * tgt,unsigned long long set)246 uisqueue_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 }
261 EXPORT_SYMBOL_GPL(uisqueue_interlocked_and);
262 
263 static u8
do_locked_client_insert(struct uisqueue_info * queueinfo,unsigned int whichqueue,void * signal,spinlock_t * lock,u8 * channel_id)264 do_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);
281 unlock:
282 	spin_unlock_irqrestore((spinlock_t *)lock, flags);
283 	return rc;
284 }
285 
286 int
uisqueue_put_cmdrsp_with_lock_client(struct uisqueue_info * queueinfo,struct uiscmdrsp * cmdrsp,unsigned int whichqueue,void * insertlock,unsigned char issue_irq_if_empty,u64 irq_handle,char oktowait,u8 * channel_id)287 uisqueue_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 }
307 EXPORT_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 */
311 int
uisqueue_get_cmdrsp(struct uisqueue_info * queueinfo,void * cmdrsp,unsigned int whichqueue)312 uisqueue_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 }
322 EXPORT_SYMBOL_GPL(uisqueue_get_cmdrsp);
323