Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 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 | |
Dan Williams | cc9203b | 2011-05-08 17:34:44 -0700 | [diff] [blame] | 56 | #include "host.h" |
Dan Williams | 63a3a15 | 2011-05-08 21:36:46 -0700 | [diff] [blame] | 57 | #include "unsolicited_frame_control.h" |
| 58 | #include "registers.h" |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 59 | |
Dan Williams | 89a7301 | 2011-06-30 19:14:33 -0700 | [diff] [blame] | 60 | int sci_unsolicited_frame_control_construct(struct isci_host *ihost) |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 61 | { |
Dan Williams | 89a7301 | 2011-06-30 19:14:33 -0700 | [diff] [blame] | 62 | struct sci_unsolicited_frame_control *uf_control = &ihost->uf_control; |
| 63 | struct sci_unsolicited_frame *uf; |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 64 | u32 buf_len, header_len, i; |
| 65 | dma_addr_t dma; |
Christoph Hellwig | bc5c967 | 2011-04-02 08:15:04 -0400 | [diff] [blame] | 66 | size_t size; |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 67 | void *virt; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 68 | |
| 69 | /* |
| 70 | * Prepare all of the memory sizes for the UF headers, UF address |
Christoph Hellwig | bc5c967 | 2011-04-02 08:15:04 -0400 | [diff] [blame] | 71 | * table, and UF buffers themselves. |
| 72 | */ |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 73 | buf_len = SCU_MAX_UNSOLICITED_FRAMES * SCU_UNSOLICITED_FRAME_BUFFER_SIZE; |
| 74 | header_len = SCU_MAX_UNSOLICITED_FRAMES * sizeof(struct scu_unsolicited_frame_header); |
Dan Williams | ee33e2b | 2011-07-29 17:16:45 -0700 | [diff] [blame] | 75 | size = buf_len + header_len + SCU_MAX_UNSOLICITED_FRAMES * sizeof(uf_control->address_table.array[0]); |
Christoph Hellwig | bc5c967 | 2011-04-02 08:15:04 -0400 | [diff] [blame] | 76 | |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 77 | /* |
| 78 | * The Unsolicited Frame buffers are set at the start of the UF |
Dave Jiang | f7885c8 | 2011-02-22 16:39:32 -0700 | [diff] [blame] | 79 | * memory descriptor entry. The headers and address table will be |
| 80 | * placed after the buffers. |
| 81 | */ |
Dan Williams | d9dcb4b | 2011-06-30 17:38:32 -0700 | [diff] [blame] | 82 | virt = dmam_alloc_coherent(&ihost->pdev->dev, size, &dma, GFP_KERNEL); |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 83 | if (!virt) |
Christoph Hellwig | bc5c967 | 2011-04-02 08:15:04 -0400 | [diff] [blame] | 84 | return -ENOMEM; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 85 | |
| 86 | /* |
| 87 | * Program the location of the UF header table into the SCU. |
| 88 | * Notes: |
| 89 | * - The address must align on a 64-byte boundary. Guaranteed to be |
| 90 | * on 64-byte boundary already 1KB boundary for unsolicited frames. |
| 91 | * - Program unused header entries to overlap with the last |
| 92 | * unsolicited frame. The silicon will never DMA to these unused |
| 93 | * headers, since we program the UF address table pointers to |
Dave Jiang | f7885c8 | 2011-02-22 16:39:32 -0700 | [diff] [blame] | 94 | * NULL. |
| 95 | */ |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 96 | uf_control->headers.physical_address = dma + buf_len; |
| 97 | uf_control->headers.array = virt + buf_len; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 98 | |
| 99 | /* |
| 100 | * Program the location of the UF address table into the SCU. |
| 101 | * Notes: |
| 102 | * - The address must align on a 64-bit boundary. Guaranteed to be on 64 |
| 103 | * byte boundary already due to above programming headers being on a |
Dave Jiang | f7885c8 | 2011-02-22 16:39:32 -0700 | [diff] [blame] | 104 | * 64-bit boundary and headers are on a 64-bytes in size. |
| 105 | */ |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 106 | uf_control->address_table.physical_address = dma + buf_len + header_len; |
| 107 | uf_control->address_table.array = virt + buf_len + header_len; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 108 | uf_control->get = 0; |
| 109 | |
| 110 | /* |
| 111 | * UF buffer requirements are: |
| 112 | * - The last entry in the UF queue is not NULL. |
| 113 | * - There is a power of 2 number of entries (NULL or not-NULL) |
| 114 | * programmed into the queue. |
| 115 | * - Aligned on a 1KB boundary. */ |
| 116 | |
| 117 | /* |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 118 | * Program the actual used UF buffers into the UF address table and |
| 119 | * the controller's array of UFs. |
| 120 | */ |
| 121 | for (i = 0; i < SCU_MAX_UNSOLICITED_FRAMES; i++) { |
| 122 | uf = &uf_control->buffers.array[i]; |
| 123 | |
| 124 | uf_control->address_table.array[i] = dma; |
| 125 | |
| 126 | uf->buffer = virt; |
| 127 | uf->header = &uf_control->headers.array[i]; |
| 128 | uf->state = UNSOLICITED_FRAME_EMPTY; |
| 129 | |
| 130 | /* |
| 131 | * Increment the address of the physical and virtual memory |
| 132 | * pointers. Everything is aligned on 1k boundary with an |
| 133 | * increment of 1k. |
| 134 | */ |
| 135 | virt += SCU_UNSOLICITED_FRAME_BUFFER_SIZE; |
| 136 | dma += SCU_UNSOLICITED_FRAME_BUFFER_SIZE; |
| 137 | } |
Christoph Hellwig | bc5c967 | 2011-04-02 08:15:04 -0400 | [diff] [blame] | 138 | |
| 139 | return 0; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 140 | } |
| 141 | |
Dan Williams | 89a7301 | 2011-06-30 19:14:33 -0700 | [diff] [blame] | 142 | enum sci_status sci_unsolicited_frame_control_get_header(struct sci_unsolicited_frame_control *uf_control, |
| 143 | u32 frame_index, |
| 144 | void **frame_header) |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 145 | { |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 146 | if (frame_index < SCU_MAX_UNSOLICITED_FRAMES) { |
Dan Williams | 89a7301 | 2011-06-30 19:14:33 -0700 | [diff] [blame] | 147 | /* Skip the first word in the frame since this is a controll word used |
| 148 | * by the hardware. |
| 149 | */ |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 150 | *frame_header = &uf_control->buffers.array[frame_index].header->data; |
| 151 | |
| 152 | return SCI_SUCCESS; |
| 153 | } |
| 154 | |
| 155 | return SCI_FAILURE_INVALID_PARAMETER_VALUE; |
| 156 | } |
| 157 | |
Dan Williams | 89a7301 | 2011-06-30 19:14:33 -0700 | [diff] [blame] | 158 | enum sci_status sci_unsolicited_frame_control_get_buffer(struct sci_unsolicited_frame_control *uf_control, |
| 159 | u32 frame_index, |
| 160 | void **frame_buffer) |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 161 | { |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 162 | if (frame_index < SCU_MAX_UNSOLICITED_FRAMES) { |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 163 | *frame_buffer = uf_control->buffers.array[frame_index].buffer; |
| 164 | |
| 165 | return SCI_SUCCESS; |
| 166 | } |
| 167 | |
| 168 | return SCI_FAILURE_INVALID_PARAMETER_VALUE; |
| 169 | } |
| 170 | |
Dan Williams | 89a7301 | 2011-06-30 19:14:33 -0700 | [diff] [blame] | 171 | bool sci_unsolicited_frame_control_release_frame(struct sci_unsolicited_frame_control *uf_control, |
| 172 | u32 frame_index) |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 173 | { |
| 174 | u32 frame_get; |
| 175 | u32 frame_cycle; |
| 176 | |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 177 | frame_get = uf_control->get & (SCU_MAX_UNSOLICITED_FRAMES - 1); |
| 178 | frame_cycle = uf_control->get & SCU_MAX_UNSOLICITED_FRAMES; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 179 | |
| 180 | /* |
| 181 | * In the event there are NULL entries in the UF table, we need to |
| 182 | * advance the get pointer in order to find out if this frame should |
Dan Williams | 994a930 | 2011-06-09 16:04:28 -0700 | [diff] [blame] | 183 | * be released (i.e. update the get pointer) |
| 184 | */ |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 185 | while (lower_32_bits(uf_control->address_table.array[frame_get]) == 0 && |
| 186 | upper_32_bits(uf_control->address_table.array[frame_get]) == 0 && |
| 187 | frame_get < SCU_MAX_UNSOLICITED_FRAMES) |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 188 | frame_get++; |
| 189 | |
| 190 | /* |
| 191 | * The table has a NULL entry as it's last element. This is |
Dan Williams | 994a930 | 2011-06-09 16:04:28 -0700 | [diff] [blame] | 192 | * illegal. |
| 193 | */ |
Dan Williams | 7c78da3 | 2011-06-01 16:00:01 -0700 | [diff] [blame] | 194 | BUG_ON(frame_get >= SCU_MAX_UNSOLICITED_FRAMES); |
Dan Williams | 994a930 | 2011-06-09 16:04:28 -0700 | [diff] [blame] | 195 | if (frame_index >= SCU_MAX_UNSOLICITED_FRAMES) |
| 196 | return false; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 197 | |
Dan Williams | 994a930 | 2011-06-09 16:04:28 -0700 | [diff] [blame] | 198 | uf_control->buffers.array[frame_index].state = UNSOLICITED_FRAME_RELEASED; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 199 | |
Dan Williams | 994a930 | 2011-06-09 16:04:28 -0700 | [diff] [blame] | 200 | if (frame_get != frame_index) { |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 201 | /* |
Dan Williams | 994a930 | 2011-06-09 16:04:28 -0700 | [diff] [blame] | 202 | * Frames remain in use until we advance the get pointer |
| 203 | * so there is nothing we can do here |
| 204 | */ |
| 205 | return false; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 206 | } |
| 207 | |
Dan Williams | 994a930 | 2011-06-09 16:04:28 -0700 | [diff] [blame] | 208 | /* |
| 209 | * The frame index is equal to the current get pointer so we |
| 210 | * can now free up all of the frame entries that |
| 211 | */ |
| 212 | while (uf_control->buffers.array[frame_get].state == UNSOLICITED_FRAME_RELEASED) { |
| 213 | uf_control->buffers.array[frame_get].state = UNSOLICITED_FRAME_EMPTY; |
Dan Williams | 6f231dd | 2011-07-02 22:56:22 -0700 | [diff] [blame] | 214 | |
Dan Williams | 994a930 | 2011-06-09 16:04:28 -0700 | [diff] [blame] | 215 | if (frame_get+1 == SCU_MAX_UNSOLICITED_FRAMES-1) { |
| 216 | frame_cycle ^= SCU_MAX_UNSOLICITED_FRAMES; |
| 217 | frame_get = 0; |
| 218 | } else |
| 219 | frame_get++; |
| 220 | } |
| 221 | |
| 222 | uf_control->get = SCU_UFQGP_GEN_BIT(ENABLE_BIT) | frame_cycle | frame_get; |
| 223 | |
| 224 | return true; |
| 225 | } |