linux/drivers/scsi/isci/unsolicited_frame_control.c
<<
>>
Prefs
   1/*
   2 * This file is provided under a dual BSD/GPLv2 license.  When using or
   3 * redistributing this file, you may do so under either license.
   4 *
   5 * GPL LICENSE SUMMARY
   6 *
   7 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of version 2 of the GNU General Public License as
  11 * published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope that it will be useful, but
  14 * WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 * General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  21 * The full GNU General Public License is included in this distribution
  22 * in the file called LICENSE.GPL.
  23 *
  24 * BSD LICENSE
  25 *
  26 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
  27 * All rights reserved.
  28 *
  29 * Redistribution and use in source and binary forms, with or without
  30 * modification, are permitted provided that the following conditions
  31 * are met:
  32 *
  33 *   * Redistributions of source code must retain the above copyright
  34 *     notice, this list of conditions and the following disclaimer.
  35 *   * Redistributions in binary form must reproduce the above copyright
  36 *     notice, this list of conditions and the following disclaimer in
  37 *     the documentation and/or other materials provided with the
  38 *     distribution.
  39 *   * Neither the name of Intel Corporation nor the names of its
  40 *     contributors may be used to endorse or promote products derived
  41 *     from this software without specific prior written permission.
  42 *
  43 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  44 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  45 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  46 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  47 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  48 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  49 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  50 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  51 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  52 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  53 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  54 */
  55
  56#include "host.h"
  57#include "unsolicited_frame_control.h"
  58#include "registers.h"
  59
  60void sci_unsolicited_frame_control_construct(struct isci_host *ihost)
  61{
  62        struct sci_unsolicited_frame_control *uf_control = &ihost->uf_control;
  63        struct sci_unsolicited_frame *uf;
  64        dma_addr_t dma = ihost->ufi_dma;
  65        void *virt = ihost->ufi_buf;
  66        int i;
  67
  68        /*
  69         * The Unsolicited Frame buffers are set at the start of the UF
  70         * memory descriptor entry. The headers and address table will be
  71         * placed after the buffers.
  72         */
  73
  74        /*
  75         * Program the location of the UF header table into the SCU.
  76         * Notes:
  77         * - The address must align on a 64-byte boundary. Guaranteed to be
  78         *   on 64-byte boundary already 1KB boundary for unsolicited frames.
  79         * - Program unused header entries to overlap with the last
  80         *   unsolicited frame.  The silicon will never DMA to these unused
  81         *   headers, since we program the UF address table pointers to
  82         *   NULL.
  83         */
  84        uf_control->headers.physical_address = dma + SCI_UFI_BUF_SIZE;
  85        uf_control->headers.array = virt + SCI_UFI_BUF_SIZE;
  86
  87        /*
  88         * Program the location of the UF address table into the SCU.
  89         * Notes:
  90         * - The address must align on a 64-bit boundary. Guaranteed to be on 64
  91         *   byte boundary already due to above programming headers being on a
  92         *   64-bit boundary and headers are on a 64-bytes in size.
  93         */
  94        uf_control->address_table.physical_address = dma + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE;
  95        uf_control->address_table.array = virt + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE;
  96        uf_control->get = 0;
  97
  98        /*
  99         * UF buffer requirements are:
 100         * - The last entry in the UF queue is not NULL.
 101         * - There is a power of 2 number of entries (NULL or not-NULL)
 102         *   programmed into the queue.
 103         * - Aligned on a 1KB boundary. */
 104
 105        /*
 106         * Program the actual used UF buffers into the UF address table and
 107         * the controller's array of UFs.
 108         */
 109        for (i = 0; i < SCU_MAX_UNSOLICITED_FRAMES; i++) {
 110                uf = &uf_control->buffers.array[i];
 111
 112                uf_control->address_table.array[i] = dma;
 113
 114                uf->buffer = virt;
 115                uf->header = &uf_control->headers.array[i];
 116                uf->state  = UNSOLICITED_FRAME_EMPTY;
 117
 118                /*
 119                 * Increment the address of the physical and virtual memory
 120                 * pointers. Everything is aligned on 1k boundary with an
 121                 * increment of 1k.
 122                 */
 123                virt += SCU_UNSOLICITED_FRAME_BUFFER_SIZE;
 124                dma += SCU_UNSOLICITED_FRAME_BUFFER_SIZE;
 125        }
 126}
 127
 128enum sci_status sci_unsolicited_frame_control_get_header(struct sci_unsolicited_frame_control *uf_control,
 129                                                         u32 frame_index,
 130                                                         void **frame_header)
 131{
 132        if (frame_index < SCU_MAX_UNSOLICITED_FRAMES) {
 133                /* Skip the first word in the frame since this is a controll word used
 134                 * by the hardware.
 135                 */
 136                *frame_header = &uf_control->buffers.array[frame_index].header->data;
 137
 138                return SCI_SUCCESS;
 139        }
 140
 141        return SCI_FAILURE_INVALID_PARAMETER_VALUE;
 142}
 143
 144enum sci_status sci_unsolicited_frame_control_get_buffer(struct sci_unsolicited_frame_control *uf_control,
 145                                                         u32 frame_index,
 146                                                         void **frame_buffer)
 147{
 148        if (frame_index < SCU_MAX_UNSOLICITED_FRAMES) {
 149                *frame_buffer = uf_control->buffers.array[frame_index].buffer;
 150
 151                return SCI_SUCCESS;
 152        }
 153
 154        return SCI_FAILURE_INVALID_PARAMETER_VALUE;
 155}
 156
 157bool sci_unsolicited_frame_control_release_frame(struct sci_unsolicited_frame_control *uf_control,
 158                                                 u32 frame_index)
 159{
 160        u32 frame_get;
 161        u32 frame_cycle;
 162
 163        frame_get   = uf_control->get & (SCU_MAX_UNSOLICITED_FRAMES - 1);
 164        frame_cycle = uf_control->get & SCU_MAX_UNSOLICITED_FRAMES;
 165
 166        /*
 167         * In the event there are NULL entries in the UF table, we need to
 168         * advance the get pointer in order to find out if this frame should
 169         * be released (i.e. update the get pointer)
 170         */
 171        while (lower_32_bits(uf_control->address_table.array[frame_get]) == 0 &&
 172               upper_32_bits(uf_control->address_table.array[frame_get]) == 0 &&
 173               frame_get < SCU_MAX_UNSOLICITED_FRAMES)
 174                frame_get++;
 175
 176        /*
 177         * The table has a NULL entry as it's last element.  This is
 178         * illegal.
 179         */
 180        BUG_ON(frame_get >= SCU_MAX_UNSOLICITED_FRAMES);
 181        if (frame_index >= SCU_MAX_UNSOLICITED_FRAMES)
 182                return false;
 183
 184        uf_control->buffers.array[frame_index].state = UNSOLICITED_FRAME_RELEASED;
 185
 186        if (frame_get != frame_index) {
 187                /*
 188                 * Frames remain in use until we advance the get pointer
 189                 * so there is nothing we can do here
 190                 */
 191                return false;
 192        }
 193
 194        /*
 195         * The frame index is equal to the current get pointer so we
 196         * can now free up all of the frame entries that
 197         */
 198        while (uf_control->buffers.array[frame_get].state == UNSOLICITED_FRAME_RELEASED) {
 199                uf_control->buffers.array[frame_get].state = UNSOLICITED_FRAME_EMPTY;
 200
 201                if (frame_get+1 == SCU_MAX_UNSOLICITED_FRAMES-1) {
 202                        frame_cycle ^= SCU_MAX_UNSOLICITED_FRAMES;
 203                        frame_get = 0;
 204                } else
 205                        frame_get++;
 206        }
 207
 208        uf_control->get = SCU_UFQGP_GEN_BIT(ENABLE_BIT) | frame_cycle | frame_get;
 209
 210        return true;
 211}
 212