Sridhar Parasuram | 7357e9b | 2014-10-01 12:19:11 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 2 | * |
| 3 | * Redistribution and use in source and binary forms, with or without |
| 4 | * modification, are permitted provided that the following conditions are |
| 5 | * met: |
| 6 | * * Redistributions of source code must retain the above copyright |
| 7 | * notice, this list of conditions and the following disclaimer. |
| 8 | * * Redistributions in binary form must reproduce the above |
| 9 | * copyright notice, this list of conditions and the following |
| 10 | * disclaimer in the documentation and/or other materials provided |
| 11 | * with the distribution. |
| 12 | * * Neither the name of The Linux Foundation nor the names of its |
| 13 | * contributors may be used to endorse or promote products derived |
| 14 | * from this software without specific prior written permission. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| 20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| 23 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| 25 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| 26 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | */ |
| 28 | |
| 29 | #include <arch/ops.h> |
| 30 | #include <sys/types.h> |
| 31 | #include <kernel/thread.h> |
| 32 | #include <debug.h> |
| 33 | #include <err.h> |
| 34 | #include <reg.h> |
| 35 | #include <string.h> |
| 36 | #include <malloc.h> |
| 37 | #include <stdlib.h> |
| 38 | #include <ufs_hw.h> |
| 39 | #include <utp.h> |
Sridhar Parasuram | 7357e9b | 2014-10-01 12:19:11 -0700 | [diff] [blame] | 40 | #include <ufs.h> |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 41 | #include <platform/iomap.h> |
| 42 | #include <platform/clock.h> |
Sridhar Parasuram | c02d107 | 2014-11-06 12:55:42 -0800 | [diff] [blame] | 43 | #include <platform/timer.h> |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 44 | #include <arch/ops.h> |
| 45 | #include <endian.h> |
| 46 | #include <stdlib.h> |
| 47 | #include <sys/types.h> |
| 48 | |
| 49 | void utp_process_req_completion(struct ufs_req_irq_type *irq) |
| 50 | { |
| 51 | struct ufs_req_node *req; |
| 52 | struct list_node *prev; |
| 53 | uint32_t val; |
| 54 | |
| 55 | /* Make sure we have more nodes than just the head in the list. */ |
| 56 | if (list_next(irq->list, irq->list) == NULL) |
| 57 | { |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 58 | dprintf(CRITICAL, "%s:%d UTRD/ UTMRD processed signalled and the wait queue is empty\n", __func__, __LINE__); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 59 | ASSERT(0); |
| 60 | } |
| 61 | |
| 62 | /* Read the door bell register. */ |
| 63 | val = readl(irq->door_bell_reg); |
| 64 | |
| 65 | list_for_every_entry(irq->list, req, struct ufs_req_node, list_node) |
| 66 | { |
| 67 | if (!(req->door_bell_bit & val)) |
| 68 | { |
| 69 | /* Transaction is complete: Either transaction completed in a normal way. |
| 70 | * Delete and Signal all requests that have completed. |
| 71 | */ |
| 72 | prev = req->list_node.prev; |
| 73 | /* TODO: move delete to the caller function. */ |
| 74 | list_delete(&(req->list_node)); |
| 75 | |
| 76 | if (event_signal(req->event, false)) |
| 77 | { |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 78 | dprintf(CRITICAL, "%s:%d Event signal failed.\n",__func__, __LINE__); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 79 | ASSERT(0); |
| 80 | |
| 81 | } |
| 82 | req = containerof(prev, struct ufs_req_node, list_node); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | return; |
| 87 | } |
| 88 | |
| 89 | /* Always called within critical section: utrd_bitmap_mutex/ utmrd_bitmap_mutex. */ |
| 90 | static uint32_t utp_get_door_bell_bit(uint32_t reg, uint32_t *reg_bitmap, uint32_t *bit_num) |
| 91 | { |
| 92 | uint32_t val = 0; |
| 93 | uint32_t doorbell_bit_val; |
| 94 | uint32_t found = 0; |
| 95 | |
| 96 | *bit_num = 0; |
| 97 | |
| 98 | val = readl(reg) | *reg_bitmap; |
| 99 | doorbell_bit_val = 1; |
| 100 | |
| 101 | /* Find an empty slot. */ |
| 102 | do |
| 103 | { |
| 104 | (*bit_num)++; |
| 105 | |
| 106 | if (!(doorbell_bit_val & val)) |
| 107 | { |
| 108 | found = 1; |
| 109 | *reg_bitmap |= doorbell_bit_val; |
| 110 | break; |
| 111 | } |
| 112 | doorbell_bit_val <<= 1; |
| 113 | }while (doorbell_bit_val <= (uint32_t) (1 << 31)); |
| 114 | |
| 115 | if (!found) |
| 116 | { |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 117 | doorbell_bit_val = 0; |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 118 | dprintf(CRITICAL, "%s:%d Unable to find a free slot for transaction.\n",__func__, __LINE__); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | return doorbell_bit_val; |
| 122 | } |
| 123 | |
| 124 | static void utp_ring_door_bell(uint32_t reg, uint32_t doorbell_bit) |
| 125 | { |
| 126 | writel(doorbell_bit, reg); |
| 127 | } |
| 128 | |
| 129 | static int utp_utrd_process_timeout_req(struct ufs_dev *dev, |
| 130 | struct utp_utrd_req_build_type *utrd_req, |
| 131 | struct ufs_req_node *req) |
| 132 | { |
| 133 | switch (utrd_req->req_upiu->trans_type) |
| 134 | { |
| 135 | case UPIU_TYPE_NOP_OUT: |
| 136 | writel(~req->door_bell_bit, UFS_UTRLCLR(dev->base)); |
| 137 | return -UFS_RETRY; |
| 138 | default: |
| 139 | /* TODO : Add ufs hci sw reset.*/ |
| 140 | ASSERT(0); |
| 141 | return -UFS_FAILURE; |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | static int utp_remove_from_bitmap(struct utp_bitmap_access_type *req) |
| 146 | { |
| 147 | |
| 148 | if (mutex_acquire(req->mutx)) |
| 149 | { |
| 150 | return -UFS_FAILURE; |
| 151 | } |
| 152 | |
| 153 | *(req->bitmap) &= ~req->door_bell_bit; |
| 154 | |
| 155 | if (mutex_release(req->mutx)) |
| 156 | { |
| 157 | return -UFS_FAILURE; |
| 158 | } |
| 159 | |
| 160 | return UFS_SUCCESS; |
| 161 | } |
| 162 | |
| 163 | static void utp_enqueue_utrd_fill_desc(struct utp_trans_req_desc *desc, struct utp_utrd_req_build_type *utrd_req) |
| 164 | { |
| 165 | /* Fill transfer desc. */ |
| 166 | memset(desc, 0, UPIU_HDR_LEN); |
| 167 | desc->cmd_type_dd_irq = UTP_REQ_BUILD_CMD_DD_IRQ_FIELD(utrd_req->cmd_type, utrd_req->dd, utrd_req->irq); |
| 168 | desc->overall_cmd_status = utrd_req->ocs; |
| 169 | /* Bits 0 - 6 are reserved in cmd_desc_base_addr[0] field. */ |
| 170 | desc->cmd_desc_base_addr[0] = ((uint32_t) utrd_req->req_upiu) & 0xC0; |
| 171 | desc->cmd_desc_base_addr[1] = ((uint32_t) utrd_req->req_upiu >> 8) & 0xFF; |
| 172 | desc->cmd_desc_base_addr[2] = ((uint32_t) utrd_req->req_upiu >> 16) & 0xFF; |
| 173 | desc->cmd_desc_base_addr[3] = ((uint32_t) utrd_req->req_upiu >> 24) & 0xFF; |
| 174 | desc->resp_upiu_offset = ROUNDUP(utrd_req->req_upiu_len, UPIU_HDR_LEN) / 4; |
| 175 | desc->resp_upiu_len = utrd_req->resp_upiu_len; |
| 176 | |
| 177 | if (utrd_req->dd != UTRD_NO_DATA_TRANSFER) |
| 178 | { |
| 179 | /* Data transfer command. |
| 180 | * Fill in PRDT data. |
| 181 | */ |
| 182 | desc->prdt_offset = utrd_req->prdt_offset / 4; |
| 183 | desc->prdt_len = utrd_req->prdt_len; |
| 184 | } |
| 185 | |
| 186 | /* Flush UTRD to memory. */ |
| 187 | cache_clean_invalidate_unaligned_start_addr((addr_t)desc, sizeof(struct utp_trans_req_desc)); |
| 188 | } |
| 189 | |
| 190 | static struct utp_trans_req_desc* utp_get_desc_slot_addr(struct ufs_dev *dev, struct utp_utrd_req_build_type *utrd_req, uint32_t *door_bell_val) |
| 191 | { |
| 192 | struct utp_trans_req_desc *desc = NULL; |
| 193 | uint32_t door_bell_slot; |
| 194 | |
| 195 | if (mutex_acquire(&(dev->utrd_data.bitmap_mutex))) |
| 196 | { |
| 197 | goto utp_get_desc_slot_addr_err; |
| 198 | } |
| 199 | |
| 200 | *door_bell_val = utp_get_door_bell_bit(UFS_UTRLDBR(dev->base), &dev->utrd_data.bitmap, &door_bell_slot); |
| 201 | if (!(*door_bell_val)) |
| 202 | { |
| 203 | goto utp_get_desc_slot_addr_err; |
| 204 | } |
| 205 | |
| 206 | if (mutex_release(&(dev->utrd_data.bitmap_mutex))) |
| 207 | { |
| 208 | goto utp_get_desc_slot_addr_err; |
| 209 | } |
| 210 | |
| 211 | desc = (struct utp_trans_req_desc *) ((addr_t)dev->utrd_data.list_base_addr + (door_bell_slot - 1) * sizeof(struct utp_trans_req_desc)); |
| 212 | |
| 213 | utp_get_desc_slot_addr_err: |
| 214 | return desc; |
| 215 | } |
| 216 | |
Sridhar Parasuram | 7357e9b | 2014-10-01 12:19:11 -0700 | [diff] [blame] | 217 | int utp_poll_utrd_complete(struct ufs_dev *dev) |
| 218 | { |
Mayank Grover | ce21a08 | 2017-10-18 14:10:50 +0530 | [diff] [blame] | 219 | int ret = ERROR; |
Sridhar Parasuram | 7357e9b | 2014-10-01 12:19:11 -0700 | [diff] [blame] | 220 | struct ufs_req_irq_type irq; |
Sridhar Parasuram | ea079c0 | 2014-10-09 16:37:27 -0700 | [diff] [blame] | 221 | uint32_t val, base, retry = 0; |
Sridhar Parasuram | 7357e9b | 2014-10-01 12:19:11 -0700 | [diff] [blame] | 222 | base = dev->base; |
| 223 | val = readl(UFS_IS(base)); |
| 224 | irq.irq_handled = 0; |
| 225 | /* Wait till the desc has been processed. */ |
| 226 | while(((val & UFS_IS_UTRCS) == 0) && ((val & UFS_IS_UTMRCS) == 0)) |
| 227 | { |
| 228 | val = readl(UFS_IS(base)); |
Sridhar Parasuram | ea079c0 | 2014-10-09 16:37:27 -0700 | [diff] [blame] | 229 | retry++; |
| 230 | udelay(1); |
| 231 | if(retry == UTP_MAX_COMMAND_RETRY) |
| 232 | { |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 233 | dprintf(CRITICAL, "%s:%d UTP command never completed.\n", __func__, __LINE__); |
| 234 | return ERR_TIMED_OUT; |
Sridhar Parasuram | ea079c0 | 2014-10-09 16:37:27 -0700 | [diff] [blame] | 235 | } |
Sridhar Parasuram | 7357e9b | 2014-10-01 12:19:11 -0700 | [diff] [blame] | 236 | #ifdef DEBUG_UFS |
| 237 | dprintf(INFO, "Waiting for UTRCS/URMRCS Completion...\n"); |
| 238 | #endif |
| 239 | } |
| 240 | if (readl(UFS_IS(base)) & UFS_IS_UTRCS) |
| 241 | { |
| 242 | val = readl(UFS_IS(base)) & UFS_IS_UTRCS; |
| 243 | writel(UFS_IS_UTRCS, UFS_IS(base)); |
| 244 | irq.irq_handled = UFS_IS_UTRCS; |
| 245 | irq.list = &(dev->utrd_data.list_head.list_node); |
| 246 | irq.door_bell_reg = UFS_UTRLDBR(base); |
| 247 | utp_process_req_completion(&irq); |
| 248 | ret = INT_NO_RESCHEDULE; |
| 249 | } |
| 250 | else if (readl(UFS_IS(base)) & (UFS_IS_UTMRCS)) |
| 251 | { |
| 252 | val = readl(UFS_IS(base)) & UFS_IS_UTMRCS; |
| 253 | writel(UFS_IS_UTMRCS, UFS_IS(base)); |
| 254 | irq.irq_handled = UFS_IS_UTMRCS; |
| 255 | irq.list = &(dev->utmrd_data.list_head.list_node); |
| 256 | utp_process_req_completion(&irq); |
| 257 | ret = INT_NO_RESCHEDULE; |
| 258 | } |
| 259 | return ret; |
| 260 | } |
| 261 | |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 262 | static int utp_enqueue_utrd(struct ufs_dev *dev, struct utp_utrd_req_build_type *utrd_req) |
| 263 | { |
| 264 | int ret; |
| 265 | struct utp_trans_req_desc *desc; |
| 266 | event_t utrd_evt; |
Mayank Grover | ce21a08 | 2017-10-18 14:10:50 +0530 | [diff] [blame] | 267 | struct ufs_req_node *req; |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 268 | uint32_t door_bell_bit_val; |
| 269 | struct utp_bitmap_access_type bitmap_req; |
| 270 | |
| 271 | ret = UFS_SUCCESS; |
Mayank Grover | ce21a08 | 2017-10-18 14:10:50 +0530 | [diff] [blame] | 272 | req = (struct ufs_req_node *)malloc(sizeof(struct ufs_req_node)); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 273 | |
| 274 | event_init(&utrd_evt, false, EVENT_FLAG_AUTOUNSIGNAL); |
| 275 | |
| 276 | desc = utp_get_desc_slot_addr(dev, utrd_req, &door_bell_bit_val); |
| 277 | if (!desc) |
| 278 | { |
| 279 | ret = UFS_FAILURE; |
| 280 | goto utp_enqueue_utrd_err; |
| 281 | } |
| 282 | |
| 283 | /* Check register UTRLRSR and make sure it is read ‘1’ before continuing. */ |
| 284 | if (!readl(UFS_UTRLRSR(dev->base))) |
| 285 | { |
| 286 | ret = -UFS_FAILURE; |
| 287 | goto utp_enqueue_utrd_err; |
| 288 | } |
| 289 | |
| 290 | utp_enqueue_utrd_fill_desc(desc, utrd_req); |
| 291 | |
Mayank Grover | ce21a08 | 2017-10-18 14:10:50 +0530 | [diff] [blame] | 292 | req->door_bell_bit = door_bell_bit_val; |
| 293 | req->event = &utrd_evt; |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 294 | |
| 295 | /* Enqueue the req in the device utrd list. */ |
Mayank Grover | ce21a08 | 2017-10-18 14:10:50 +0530 | [diff] [blame] | 296 | list_add_head(&(dev->utrd_data.list_head.list_node), &(req->list_node)); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 297 | |
| 298 | dsb(); |
| 299 | |
Sridhar Parasuram | 7357e9b | 2014-10-01 12:19:11 -0700 | [diff] [blame] | 300 | #ifdef DEBUG_UFS |
| 301 | // print IS before write |
| 302 | ufs_dump_is_register(dev); |
| 303 | #endif |
| 304 | |
| 305 | utp_ring_door_bell(UFS_UTRLDBR(dev->base), door_bell_bit_val); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 306 | |
| 307 | dsb(); |
| 308 | |
Sridhar Parasuram | 7357e9b | 2014-10-01 12:19:11 -0700 | [diff] [blame] | 309 | #ifdef DEBUG_UFS |
| 310 | // print IS after write |
| 311 | ufs_dump_is_register(dev); |
| 312 | #endif |
| 313 | ret = utp_poll_utrd_complete(dev); |
| 314 | |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 315 | if (ret == ERR_TIMED_OUT) |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 316 | { |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 317 | /* Transaction not completed even after timeout ms. */ |
| 318 | dprintf(CRITICAL, "%s:%d Transaction timeout after polling %d times\n",__func__, __LINE__, UTP_MAX_COMMAND_RETRY); |
Mayank Grover | ce21a08 | 2017-10-18 14:10:50 +0530 | [diff] [blame] | 319 | ret = utp_utrd_process_timeout_req(dev, utrd_req, req); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 320 | goto utp_enqueue_utrd_err; |
| 321 | } |
| 322 | else |
| 323 | { |
| 324 | /* Reset ret before returning. */ |
| 325 | ret = UFS_SUCCESS; |
| 326 | |
| 327 | /* Force read UTRD from memory. */ |
| 328 | dsb(); |
| 329 | cache_clean_invalidate_unaligned_start_addr((addr_t) desc, sizeof(struct ufs_req_node)); |
| 330 | |
| 331 | /* Check the response. */ |
| 332 | if (desc->overall_cmd_status != UTRD_OCS_SUCCESS) |
| 333 | { |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 334 | dprintf(CRITICAL, "%s:%d Command failed. command type = %x\n", __func__, __LINE__, utrd_req->cmd_type); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 335 | ret = -UFS_FAILURE; |
| 336 | goto utp_enqueue_utrd_err; |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | /* Signal slot as free. */ |
| 341 | bitmap_req.bitmap = &dev->utrd_data.bitmap; |
Mayank Grover | ce21a08 | 2017-10-18 14:10:50 +0530 | [diff] [blame] | 342 | bitmap_req.door_bell_bit = req->door_bell_bit; |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 343 | bitmap_req.mutx = &(dev->utrd_data.bitmap_mutex); |
| 344 | |
| 345 | ret = utp_remove_from_bitmap(&bitmap_req); |
| 346 | if (ret) |
| 347 | goto utp_enqueue_utrd_err; |
| 348 | |
| 349 | utp_enqueue_utrd_err: |
| 350 | return ret; |
| 351 | } |
| 352 | |
| 353 | static int utp_get_prdt_len(uint32_t data_len, uint32_t *num_prdt) |
| 354 | { |
| 355 | /* Calculate the prdt entries required. */ |
| 356 | *num_prdt = ROUNDUP(data_len, UTP_MAX_PRD_DATA_BYTE_CNT); |
| 357 | *num_prdt >>= UTP_MAX_PRD_DATA_BYTE_CNT_BYTE_SHIFT; |
| 358 | |
| 359 | if (*num_prdt > UTP_MAX_PRD_TABLE_ENTRIES) |
| 360 | { |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 361 | dprintf(CRITICAL, "%s:%d Data length exceeds for a single upiu transfer.\n", __func__,__LINE__); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 362 | return -UFS_FAILURE; |
| 363 | } |
| 364 | |
| 365 | return UFS_SUCCESS; |
| 366 | } |
| 367 | |
| 368 | static int utp_fill_req_upiu(struct ufs_dev *dev, struct upiu_req_build_type *upiu_data, struct upiu_gen_hdr *req_upiu) |
| 369 | { |
| 370 | memset(req_upiu, 0, UPIU_HDR_LEN); |
| 371 | |
| 372 | if (upiu_data->trans_type == UPIU_TYPE_QUERY_REQ) |
| 373 | { |
| 374 | /* Fill in query specific fields. */ |
| 375 | if (utp_build_query_req_upiu((struct upiu_trans_mgmt_query_hdr *) req_upiu, upiu_data)) |
| 376 | { |
| 377 | return -UFS_FAILURE; |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | /* If a data transfer cmd, check the alignment and length of the buffer. */ |
| 382 | if (upiu_data->expected_data_len) |
| 383 | { |
| 384 | if (upiu_data->data_buffer_addr & 0x3) |
| 385 | { |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 386 | dprintf(CRITICAL, "%s:%d Alignment and length check failed for data tranfer command.\n", __func__, __LINE__); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 387 | return -UFS_FAILURE; |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | req_upiu->basic_hdr.trans_type = upiu_data->trans_type; |
| 392 | req_upiu->basic_hdr.flags = upiu_data->flags; |
| 393 | req_upiu->basic_hdr.cmd_set_type = upiu_data->cmd_set_type; |
| 394 | req_upiu->basic_hdr.data_seg_len = upiu_data->data_seg_len; |
| 395 | req_upiu->basic_hdr.lun = upiu_data->lun; |
| 396 | req_upiu->basic_hdr.total_ehs_len = upiu_data->ehs_len; |
| 397 | req_upiu->basic_hdr.task_tag = atomic_add((int *) &(dev->utrd_data.task_id), 1); |
| 398 | if (upiu_data->cdb) |
| 399 | memcpy(&(req_upiu->trans_specific_fields[4]), (void *) upiu_data->cdb, 16); |
| 400 | /* If command upiu, fill in data length. */ |
| 401 | if (req_upiu->basic_hdr.trans_type == UPIU_TYPE_COMMAND) |
| 402 | ((struct upiu_cmd_hdr *)req_upiu)->data_expected_len = BE32(upiu_data->expected_data_len); |
| 403 | |
| 404 | return UFS_SUCCESS; |
| 405 | |
| 406 | } |
| 407 | |
| 408 | static void utp_fill_utrd_properties(struct upiu_req_build_type *upiu_data, |
| 409 | struct utp_utrd_req_build_type *utrd, |
| 410 | struct utrd_cmd_desc *cmd_desc) |
| 411 | { |
| 412 | utrd->cmd_type = upiu_data->cmd_type; |
| 413 | utrd->dd = upiu_data->dd; |
| 414 | utrd->irq = UTRD_IRQ_CMD; |
| 415 | utrd->prdt_offset = UPIU_HDR_LEN + cmd_desc->resp_upiu_len; |
| 416 | utrd->prdt_len = cmd_desc->num_prdt; |
| 417 | utrd->req_upiu = (struct upiu_basic_hdr *) cmd_desc->req_upiu; |
| 418 | utrd->req_upiu_len = UPIU_HDR_LEN; |
| 419 | utrd->resp_upiu_len = cmd_desc->resp_upiu_len; |
| 420 | utrd->ocs = 0xF; |
| 421 | utrd->timeout = upiu_data->timeout_msecs; |
| 422 | } |
| 423 | |
| 424 | static void utp_fill_prdt_entries(struct upiu_req_build_type *upiu_data, struct utp_prdt_entry *prdt_entry) |
| 425 | { |
| 426 | uint64_t buf; |
| 427 | uint64_t bytes_remaining; |
| 428 | uint32_t prd_dbc; |
| 429 | |
| 430 | buf = upiu_data->data_buffer_addr; |
| 431 | bytes_remaining = upiu_data->expected_data_len; |
| 432 | |
| 433 | while (bytes_remaining) |
| 434 | { |
| 435 | prdt_entry->data_base_addr = buf; |
| 436 | prdt_entry->data_upper_addr = buf >> 32; |
| 437 | prd_dbc = MIN(UTP_MAX_PRD_DATA_BYTE_CNT, bytes_remaining) - 1; |
| 438 | prdt_entry->data_byte_cnt = prd_dbc; |
| 439 | if (bytes_remaining <= UTP_MAX_PRD_DATA_BYTE_CNT) |
| 440 | break; |
| 441 | buf += UTP_MAX_PRD_DATA_BYTE_CNT; |
| 442 | bytes_remaining -= UTP_MAX_PRD_DATA_BYTE_CNT; |
| 443 | prdt_entry++; |
| 444 | } |
| 445 | |
| 446 | } |
| 447 | |
| 448 | int utp_enqueue_upiu(struct ufs_dev *dev, struct upiu_req_build_type *upiu_data) |
| 449 | { |
| 450 | struct upiu_gen_hdr *req_upiu; |
| 451 | struct utp_utrd_req_build_type utrd; |
| 452 | uint32_t num_prdt; |
| 453 | struct utp_prdt_entry *prdt_entry; |
| 454 | int ret = UFS_SUCCESS; |
| 455 | uint32_t resp_len; |
| 456 | uint32_t cmd_desc_len; |
| 457 | struct utrd_cmd_desc cmd_desc; |
| 458 | |
| 459 | /* Round up resp_upiu_len to a DWORD boundary. |
| 460 | * Also, make sure it is of min required length. |
| 461 | */ |
| 462 | resp_len = ROUNDUP(upiu_data->resp_data_len, 4) + UPIU_HDR_LEN; |
| 463 | |
| 464 | if (utp_get_prdt_len(upiu_data->expected_data_len, &num_prdt)) |
| 465 | return -UFS_FAILURE; |
| 466 | |
| 467 | /* Calculate the length. */ |
| 468 | cmd_desc_len = UPIU_HDR_LEN + resp_len + num_prdt * sizeof(struct utp_prdt_entry); |
| 469 | |
| 470 | /* Allocate memory for UTP Command Descriptor. */ |
| 471 | req_upiu = (struct upiu_gen_hdr*) memalign((size_t ) lcm(CACHE_LINE, UTP_CMD_DESC_BASE_ALIGNMENT_SIZE), ROUNDUP(cmd_desc_len, CACHE_LINE)); |
| 472 | if (!req_upiu) |
| 473 | { |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 474 | dprintf(CRITICAL, "%s:%d Unable to allocate request upiu\n",__func__, __LINE__); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 475 | return -UFS_FAILURE; |
| 476 | } |
| 477 | |
| 478 | /* Fill req upiu. */ |
| 479 | ret = utp_fill_req_upiu(dev, upiu_data, req_upiu); |
| 480 | if (ret) |
| 481 | { |
| 482 | goto utp_enqueue_upiu_err; |
| 483 | } |
| 484 | |
| 485 | /* Fill UTRD properties. */ |
| 486 | cmd_desc.num_prdt = num_prdt; |
| 487 | cmd_desc.req_upiu = req_upiu; |
| 488 | cmd_desc.resp_upiu_len = resp_len; |
| 489 | utp_fill_utrd_properties(upiu_data, &utrd, &cmd_desc); |
| 490 | |
| 491 | prdt_entry = (struct utp_prdt_entry *) ((uint32_t) req_upiu + UPIU_HDR_LEN + resp_len); |
| 492 | |
| 493 | /* Fill PRDT entries. */ |
| 494 | if (num_prdt) |
| 495 | utp_fill_prdt_entries(upiu_data, prdt_entry); |
| 496 | |
| 497 | /* Flush req_upiu */ |
| 498 | dsb(); |
| 499 | arch_clean_invalidate_cache_range((addr_t) req_upiu, cmd_desc_len); |
| 500 | |
| 501 | /* Check the response. */ |
| 502 | ret = utp_enqueue_utrd(dev, &utrd); |
| 503 | if (ret) |
| 504 | { |
Sridhar Parasuram | 56d09f3 | 2014-11-05 15:58:30 -0800 | [diff] [blame] | 505 | dprintf(CRITICAL, "%s:%d Command failed. command = %x\n", __func__, __LINE__, req_upiu->basic_hdr.trans_type); |
Deepa Dinamani | ab9c2b9 | 2013-09-12 11:30:58 -0700 | [diff] [blame] | 506 | goto utp_enqueue_upiu_err; |
| 507 | } |
| 508 | |
| 509 | /* UPIU processed. Invalidate cache to update resp. */ |
| 510 | arch_invalidate_cache_range((addr_t) req_upiu, cmd_desc_len); |
| 511 | |
| 512 | /* Save the response. */ |
| 513 | memcpy(upiu_data->resp_ptr, (void *) ((uint32_t)req_upiu + UPIU_HDR_LEN), upiu_data->resp_len); |
| 514 | memcpy((void *) upiu_data->resp_data_ptr, (void *) ((uint32_t)req_upiu + 2 * UPIU_HDR_LEN), upiu_data->resp_data_len); |
| 515 | |
| 516 | utp_enqueue_upiu_err: |
| 517 | free(req_upiu); |
| 518 | return ret; |
| 519 | } |