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