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