diff options
Diffstat (limited to 'drivers/staging/unisys/uislib/uisqueue.c')
-rw-r--r-- | drivers/staging/unisys/uislib/uisqueue.c | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/drivers/staging/unisys/uislib/uisqueue.c b/drivers/staging/unisys/uislib/uisqueue.c new file mode 100644 index 000000000..d46dd7428 --- /dev/null +++ b/drivers/staging/unisys/uislib/uisqueue.c @@ -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); |