/* 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);