Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / unisys / uislib / uisqueue.c
diff --git a/kernel/drivers/staging/unisys/uislib/uisqueue.c b/kernel/drivers/staging/unisys/uislib/uisqueue.c
new file mode 100644 (file)
index 0000000..d46dd74
--- /dev/null
@@ -0,0 +1,322 @@
+/* uisqueue.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ */
+
+/* @ALL_INSPECTED */
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "uisutils.h"
+
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages */
+#define CURRENT_FILE_PC UISLIB_PC_uisqueue_c
+#define __MYFILE__ "uisqueue.c"
+
+#define CHECK_CACHE_ALIGN 0
+
+/*****************************************************/
+/* Exported functions                                */
+/*****************************************************/
+
+/*
+ * Routine Description:
+ * Tries to insert the prebuilt signal pointed to by pSignal into the nth
+ * Queue of the Channel pointed to by pChannel
+ *
+ * Parameters:
+ * pChannel: (IN) points to the IO Channel
+ * Queue: (IN) nth Queue of the IO Channel
+ * pSignal: (IN) pointer to the signal
+ *
+ * Assumptions:
+ * - pChannel, Queue and pSignal are valid.
+ * - If insertion fails due to a full queue, the caller will determine the
+ * retry policy (e.g. wait & try again, report an error, etc.).
+ *
+ * Return value:
+ * 1 if the insertion succeeds, 0 if the queue was full.
+ */
+unsigned char spar_signal_insert(struct channel_header __iomem *ch, u32 queue,
+                                void *sig)
+{
+       void __iomem *psignal;
+       unsigned int head, tail, nof;
+
+       struct signal_queue_header __iomem *pqhdr =
+           (struct signal_queue_header __iomem *)
+               ((char __iomem *)ch + readq(&ch->ch_space_offset))
+               + queue;
+
+       /* capture current head and tail */
+       head = readl(&pqhdr->head);
+       tail = readl(&pqhdr->tail);
+
+       /* queue is full if (head + 1) % n equals tail */
+       if (((head + 1) % readl(&pqhdr->max_slots)) == tail) {
+               nof = readq(&pqhdr->num_overflows) + 1;
+               writeq(nof, &pqhdr->num_overflows);
+               return 0;
+       }
+
+       /* increment the head index */
+       head = (head + 1) % readl(&pqhdr->max_slots);
+
+       /* copy signal to the head location from the area pointed to
+        * by pSignal
+        */
+       psignal = (char __iomem *)pqhdr + readq(&pqhdr->sig_base_offset) +
+               (head * readl(&pqhdr->signal_size));
+       memcpy_toio(psignal, sig, readl(&pqhdr->signal_size));
+
+       mb(); /* channel synch */
+       writel(head, &pqhdr->head);
+
+       writeq(readq(&pqhdr->num_sent) + 1, &pqhdr->num_sent);
+       return 1;
+}
+EXPORT_SYMBOL_GPL(spar_signal_insert);
+
+/*
+ * Routine Description:
+ * Removes one signal from Channel pChannel's nth Queue at the
+ * time of the call and copies it into the memory pointed to by
+ * pSignal.
+ *
+ * Parameters:
+ * pChannel: (IN) points to the IO Channel
+ * Queue: (IN) nth Queue of the IO Channel
+ * pSignal: (IN) pointer to where the signals are to be copied
+ *
+ * Assumptions:
+ * - pChannel and Queue are valid.
+ * - pSignal points to a memory area large enough to hold queue's SignalSize
+ *
+ * Return value:
+ * 1 if the removal succeeds, 0 if the queue was empty.
+ */
+unsigned char
+spar_signal_remove(struct channel_header __iomem *ch, u32 queue, void *sig)
+{
+       void __iomem *psource;
+       unsigned int head, tail;
+       struct signal_queue_header __iomem *pqhdr =
+           (struct signal_queue_header __iomem *)((char __iomem *)ch +
+                                   readq(&ch->ch_space_offset)) + queue;
+
+       /* capture current head and tail */
+       head = readl(&pqhdr->head);
+       tail = readl(&pqhdr->tail);
+
+       /* queue is empty if the head index equals the tail index */
+       if (head == tail) {
+               writeq(readq(&pqhdr->num_empty) + 1, &pqhdr->num_empty);
+               return 0;
+       }
+
+       /* advance past the 'empty' front slot */
+       tail = (tail + 1) % readl(&pqhdr->max_slots);
+
+       /* copy signal from tail location to the area pointed to by pSignal */
+       psource = (char __iomem *)pqhdr + readq(&pqhdr->sig_base_offset) +
+               (tail * readl(&pqhdr->signal_size));
+       memcpy_fromio(sig, psource, readl(&pqhdr->signal_size));
+
+       mb(); /* channel synch */
+       writel(tail, &pqhdr->tail);
+
+       writeq(readq(&pqhdr->num_received) + 1,
+              &pqhdr->num_received);
+       return 1;
+}
+EXPORT_SYMBOL_GPL(spar_signal_remove);
+
+/*
+ * Routine Description:
+ * Removes all signals present in Channel pChannel's nth Queue at the
+ * time of the call and copies them into the memory pointed to by
+ * pSignal.  Returns the # of signals copied as the value of the routine.
+ *
+ * Parameters:
+ * pChannel: (IN) points to the IO Channel
+ * Queue: (IN) nth Queue of the IO Channel
+ * pSignal: (IN) pointer to where the signals are to be copied
+ *
+ * Assumptions:
+ * - pChannel and Queue are valid.
+ * - pSignal points to a memory area large enough to hold Queue's MaxSignals
+ * # of signals, each of which is Queue's SignalSize.
+ *
+ * Return value:
+ * # of signals copied.
+ */
+unsigned int spar_signal_remove_all(struct channel_header *ch, u32 queue,
+                                   void *sig)
+{
+       void *psource;
+       unsigned int head, tail, count = 0;
+       struct signal_queue_header *pqhdr =
+           (struct signal_queue_header *)((char *)ch +
+                                   ch->ch_space_offset) + queue;
+
+       /* capture current head and tail */
+       head = pqhdr->head;
+       tail = pqhdr->tail;
+
+       /* queue is empty if the head index equals the tail index */
+       if (head == tail)
+               return 0;
+
+       while (head != tail) {
+               /* advance past the 'empty' front slot */
+               tail = (tail + 1) % pqhdr->max_slots;
+
+               /* copy signal from tail location to the area pointed
+                * to by pSignal
+                */
+               psource =
+                   (char *)pqhdr + pqhdr->sig_base_offset +
+                   (tail * pqhdr->signal_size);
+               memcpy((char *)sig + (pqhdr->signal_size * count),
+                      psource, pqhdr->signal_size);
+
+               mb(); /* channel synch */
+               pqhdr->tail = tail;
+
+               count++;
+               pqhdr->num_received++;
+       }
+
+       return count;
+}
+
+/*
+ * Routine Description:
+ * Determine whether a signal queue is empty.
+ *
+ * Parameters:
+ * pChannel: (IN) points to the IO Channel
+ * Queue: (IN) nth Queue of the IO Channel
+ *
+ * Return value:
+ * 1 if the signal queue is empty, 0 otherwise.
+ */
+unsigned char spar_signalqueue_empty(struct channel_header __iomem *ch,
+                                    u32 queue)
+{
+       struct signal_queue_header __iomem *pqhdr =
+           (struct signal_queue_header __iomem *)((char __iomem *)ch +
+                                   readq(&ch->ch_space_offset)) + queue;
+       return readl(&pqhdr->head) == readl(&pqhdr->tail);
+}
+EXPORT_SYMBOL_GPL(spar_signalqueue_empty);
+
+unsigned long long
+uisqueue_interlocked_or(unsigned long long __iomem *tgt,
+                       unsigned long long set)
+{
+       unsigned long long i;
+       unsigned long long j;
+
+       j = readq(tgt);
+       do {
+               i = j;
+               j = cmpxchg((__force unsigned long long *)tgt, i, i | set);
+
+       } while (i != j);
+
+       return j;
+}
+EXPORT_SYMBOL_GPL(uisqueue_interlocked_or);
+
+unsigned long long
+uisqueue_interlocked_and(unsigned long long __iomem *tgt,
+                        unsigned long long set)
+{
+       unsigned long long i;
+       unsigned long long j;
+
+       j = readq(tgt);
+       do {
+               i = j;
+               j = cmpxchg((__force unsigned long long *)tgt, i, i & set);
+
+       } while (i != j);
+
+       return j;
+}
+EXPORT_SYMBOL_GPL(uisqueue_interlocked_and);
+
+static u8
+do_locked_client_insert(struct uisqueue_info *queueinfo,
+                       unsigned int whichqueue,
+                       void *signal,
+                       spinlock_t *lock,
+                       u8 *channel_id)
+{
+       unsigned long flags;
+       u8 rc = 0;
+
+       spin_lock_irqsave(lock, flags);
+       if (!spar_channel_client_acquire_os(queueinfo->chan, channel_id))
+               goto unlock;
+       if (spar_signal_insert(queueinfo->chan, whichqueue, signal)) {
+               queueinfo->packets_sent++;
+               rc = 1;
+       }
+       spar_channel_client_release_os(queueinfo->chan, channel_id);
+unlock:
+       spin_unlock_irqrestore((spinlock_t *)lock, flags);
+       return rc;
+}
+
+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)
+{
+       while (!do_locked_client_insert(queueinfo, whichqueue, cmdrsp,
+                                       (spinlock_t *)insertlock,
+                                       channel_id)) {
+               if (oktowait != OK_TO_WAIT)
+                       return 0;       /* failed to queue */
+
+               /* try again */
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(msecs_to_jiffies(10));
+       }
+       return 1;
+}
+EXPORT_SYMBOL_GPL(uisqueue_put_cmdrsp_with_lock_client);
+
+/* uisqueue_get_cmdrsp gets the cmdrsp entry at the head of the queue
+ * returns NULL if queue is empty */
+int
+uisqueue_get_cmdrsp(struct uisqueue_info *queueinfo,
+                   void *cmdrsp, unsigned int whichqueue)
+{
+       if (!spar_signal_remove(queueinfo->chan, whichqueue, cmdrsp))
+               return 0;
+
+       queueinfo->packets_received++;
+
+       return 1;               /* Success */
+}
+EXPORT_SYMBOL_GPL(uisqueue_get_cmdrsp);