Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 1 | //------------------------------------------------------------------------------ |
| 2 | // Copyright (c) 2009-2010 Atheros Corporation. All rights reserved. |
| 3 | // |
| 4 | // |
| 5 | // Permission to use, copy, modify, and/or distribute this software for any |
| 6 | // purpose with or without fee is hereby granted, provided that the above |
| 7 | // copyright notice and this permission notice appear in all copies. |
| 8 | // |
| 9 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 10 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 11 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 12 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 13 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 14 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 15 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 16 | // |
| 17 | // |
| 18 | //------------------------------------------------------------------------------ |
| 19 | //============================================================================== |
| 20 | // HIF scatter implementation |
| 21 | // |
| 22 | // Author(s): ="Atheros" |
| 23 | //============================================================================== |
| 24 | |
| 25 | #include <linux/mmc/card.h> |
| 26 | #include <linux/mmc/host.h> |
| 27 | #include <linux/mmc/sdio_func.h> |
| 28 | #include <linux/mmc/sdio_ids.h> |
| 29 | #include <linux/mmc/sdio.h> |
| 30 | #include <linux/kthread.h> |
| 31 | #include "hif_internal.h" |
| 32 | #define ATH_MODULE_NAME hif |
| 33 | #include "a_debug.h" |
| 34 | |
| 35 | #ifdef HIF_LINUX_MMC_SCATTER_SUPPORT |
| 36 | |
| 37 | #define _CMD53_ARG_READ 0 |
| 38 | #define _CMD53_ARG_WRITE 1 |
| 39 | #define _CMD53_ARG_BLOCK_BASIS 1 |
| 40 | #define _CMD53_ARG_FIXED_ADDRESS 0 |
| 41 | #define _CMD53_ARG_INCR_ADDRESS 1 |
| 42 | |
| 43 | #define SDIO_SET_CMD53_ARG(arg,rw,func,mode,opcode,address,bytes_blocks) \ |
| 44 | (arg) = (((rw) & 1) << 31) | \ |
| 45 | (((func) & 0x7) << 28) | \ |
| 46 | (((mode) & 1) << 27) | \ |
| 47 | (((opcode) & 1) << 26) | \ |
| 48 | (((address) & 0x1FFFF) << 9) | \ |
| 49 | ((bytes_blocks) & 0x1FF) |
| 50 | |
| 51 | static void FreeScatterReq(HIF_DEVICE *device, HIF_SCATTER_REQ *pReq) |
| 52 | { |
| 53 | unsigned long flag; |
| 54 | |
| 55 | spin_lock_irqsave(&device->lock, flag); |
| 56 | |
| 57 | DL_ListInsertTail(&device->ScatterReqHead, &pReq->ListLink); |
| 58 | |
| 59 | spin_unlock_irqrestore(&device->lock, flag); |
| 60 | |
| 61 | } |
| 62 | |
| 63 | static HIF_SCATTER_REQ *AllocScatterReq(HIF_DEVICE *device) |
| 64 | { |
| 65 | DL_LIST *pItem; |
| 66 | unsigned long flag; |
| 67 | |
| 68 | spin_lock_irqsave(&device->lock, flag); |
| 69 | |
| 70 | pItem = DL_ListRemoveItemFromHead(&device->ScatterReqHead); |
| 71 | |
| 72 | spin_unlock_irqrestore(&device->lock, flag); |
| 73 | |
| 74 | if (pItem != NULL) { |
| 75 | return A_CONTAINING_STRUCT(pItem, HIF_SCATTER_REQ, ListLink); |
| 76 | } |
| 77 | |
| 78 | return NULL; |
| 79 | } |
| 80 | |
| 81 | /* called by async task to perform the operation synchronously using direct MMC APIs */ |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 82 | int DoHifReadWriteScatter(HIF_DEVICE *device, BUS_REQUEST *busrequest) |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 83 | { |
| 84 | int i; |
Joe Perches | ab3655d | 2011-02-02 14:05:49 -0800 | [diff] [blame^] | 85 | u8 rw; |
| 86 | u8 opcode; |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 87 | struct mmc_request mmcreq; |
| 88 | struct mmc_command cmd; |
| 89 | struct mmc_data data; |
| 90 | HIF_SCATTER_REQ_PRIV *pReqPriv; |
| 91 | HIF_SCATTER_REQ *pReq; |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 92 | int status = A_OK; |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 93 | struct scatterlist *pSg; |
| 94 | |
| 95 | pReqPriv = busrequest->pScatterReq; |
| 96 | |
| 97 | A_ASSERT(pReqPriv != NULL); |
| 98 | |
| 99 | pReq = pReqPriv->pHifScatterReq; |
| 100 | |
| 101 | memset(&mmcreq, 0, sizeof(struct mmc_request)); |
| 102 | memset(&cmd, 0, sizeof(struct mmc_command)); |
| 103 | memset(&data, 0, sizeof(struct mmc_data)); |
| 104 | |
| 105 | data.blksz = HIF_MBOX_BLOCK_SIZE; |
| 106 | data.blocks = pReq->TotalLength / HIF_MBOX_BLOCK_SIZE; |
| 107 | |
| 108 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: (%s) Address: 0x%X, (BlockLen: %d, BlockCount: %d) , (tot:%d,sg:%d)\n", |
| 109 | (pReq->Request & HIF_WRITE) ? "WRITE":"READ", pReq->Address, data.blksz, data.blocks, |
| 110 | pReq->TotalLength,pReq->ValidScatterEntries)); |
| 111 | |
| 112 | if (pReq->Request & HIF_WRITE) { |
| 113 | rw = _CMD53_ARG_WRITE; |
| 114 | data.flags = MMC_DATA_WRITE; |
| 115 | } else { |
| 116 | rw = _CMD53_ARG_READ; |
| 117 | data.flags = MMC_DATA_READ; |
| 118 | } |
| 119 | |
| 120 | if (pReq->Request & HIF_FIXED_ADDRESS) { |
| 121 | opcode = _CMD53_ARG_FIXED_ADDRESS; |
| 122 | } else { |
| 123 | opcode = _CMD53_ARG_INCR_ADDRESS; |
| 124 | } |
| 125 | |
| 126 | /* fill SG entries */ |
| 127 | pSg = pReqPriv->sgentries; |
| 128 | sg_init_table(pSg, pReq->ValidScatterEntries); |
| 129 | |
| 130 | /* assemble SG list */ |
| 131 | for (i = 0 ; i < pReq->ValidScatterEntries ; i++, pSg++) { |
| 132 | /* setup each sg entry */ |
| 133 | if ((unsigned long)pReq->ScatterList[i].pBuffer & 0x3) { |
| 134 | /* note some scatter engines can handle unaligned buffers, print this |
| 135 | * as informational only */ |
| 136 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, |
| 137 | ("HIF: (%s) Scatter Buffer is unaligned 0x%lx\n", |
| 138 | pReq->Request & HIF_WRITE ? "WRITE":"READ", |
| 139 | (unsigned long)pReq->ScatterList[i].pBuffer)); |
| 140 | } |
| 141 | |
| 142 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, (" %d: Addr:0x%lX, Len:%d \n", |
| 143 | i,(unsigned long)pReq->ScatterList[i].pBuffer,pReq->ScatterList[i].Length)); |
| 144 | |
| 145 | sg_set_buf(pSg, pReq->ScatterList[i].pBuffer, pReq->ScatterList[i].Length); |
| 146 | } |
| 147 | /* set scatter-gather table for request */ |
| 148 | data.sg = pReqPriv->sgentries; |
| 149 | data.sg_len = pReq->ValidScatterEntries; |
| 150 | /* set command argument */ |
| 151 | SDIO_SET_CMD53_ARG(cmd.arg, |
| 152 | rw, |
| 153 | device->func->num, |
| 154 | _CMD53_ARG_BLOCK_BASIS, |
| 155 | opcode, |
| 156 | pReq->Address, |
| 157 | data.blocks); |
| 158 | |
| 159 | cmd.opcode = SD_IO_RW_EXTENDED; |
| 160 | cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; |
| 161 | |
| 162 | mmcreq.cmd = &cmd; |
| 163 | mmcreq.data = &data; |
| 164 | |
| 165 | mmc_set_data_timeout(&data, device->func->card); |
| 166 | /* synchronous call to process request */ |
| 167 | mmc_wait_for_req(device->func->card->host, &mmcreq); |
| 168 | |
| 169 | if (cmd.error) { |
| 170 | status = A_ERROR; |
| 171 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: cmd error: %d \n",cmd.error)); |
| 172 | } |
| 173 | |
| 174 | if (data.error) { |
| 175 | status = A_ERROR; |
| 176 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: data error: %d \n",data.error)); |
| 177 | } |
| 178 | |
Joe Perches | 391bb21 | 2011-01-27 20:04:21 -0800 | [diff] [blame] | 179 | if (status) { |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 180 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: FAILED!!! (%s) Address: 0x%X, Block mode (BlockLen: %d, BlockCount: %d)\n", |
| 181 | (pReq->Request & HIF_WRITE) ? "WRITE":"READ",pReq->Address, data.blksz, data.blocks)); |
| 182 | } |
| 183 | |
| 184 | /* set completion status, fail or success */ |
| 185 | pReq->CompletionStatus = status; |
| 186 | |
| 187 | if (pReq->Request & HIF_ASYNCHRONOUS) { |
| 188 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: async_task completion routine req: 0x%lX (%d)\n",(unsigned long)busrequest, status)); |
| 189 | /* complete the request */ |
| 190 | A_ASSERT(pReq->CompletionRoutine != NULL); |
| 191 | pReq->CompletionRoutine(pReq); |
| 192 | } else { |
| 193 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER async_task upping busrequest : 0x%lX (%d)\n", (unsigned long)busrequest,status)); |
| 194 | /* signal wait */ |
| 195 | up(&busrequest->sem_req); |
| 196 | } |
| 197 | |
| 198 | return status; |
| 199 | } |
| 200 | |
| 201 | /* callback to issue a read-write scatter request */ |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 202 | static int HifReadWriteScatter(HIF_DEVICE *device, HIF_SCATTER_REQ *pReq) |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 203 | { |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 204 | int status = A_EINVAL; |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 205 | A_UINT32 request = pReq->Request; |
| 206 | HIF_SCATTER_REQ_PRIV *pReqPriv = (HIF_SCATTER_REQ_PRIV *)pReq->HIFPrivate[0]; |
| 207 | |
| 208 | do { |
| 209 | |
| 210 | A_ASSERT(pReqPriv != NULL); |
| 211 | |
| 212 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: total len: %d Scatter Entries: %d\n", |
| 213 | pReq->TotalLength, pReq->ValidScatterEntries)); |
| 214 | |
| 215 | if (!(request & HIF_EXTENDED_IO)) { |
| 216 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
| 217 | ("HIF-SCATTER: Invalid command type: 0x%08x\n", request)); |
| 218 | break; |
| 219 | } |
| 220 | |
| 221 | if (!(request & (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS))) { |
| 222 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
| 223 | ("HIF-SCATTER: Invalid execution mode: 0x%08x\n", request)); |
| 224 | break; |
| 225 | } |
| 226 | |
| 227 | if (!(request & HIF_BLOCK_BASIS)) { |
| 228 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
| 229 | ("HIF-SCATTER: Invalid data mode: 0x%08x\n", request)); |
| 230 | break; |
| 231 | } |
| 232 | |
| 233 | if (pReq->TotalLength > MAX_SCATTER_REQ_TRANSFER_SIZE) { |
| 234 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
| 235 | ("HIF-SCATTER: Invalid length: %d \n", pReq->TotalLength)); |
| 236 | break; |
| 237 | } |
| 238 | |
| 239 | if (pReq->TotalLength == 0) { |
Joe Perches | 1071a13 | 2011-02-02 14:05:47 -0800 | [diff] [blame] | 240 | A_ASSERT(false); |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 241 | break; |
| 242 | } |
| 243 | |
| 244 | /* add bus request to the async list for the async I/O thread to process */ |
| 245 | AddToAsyncList(device, pReqPriv->busrequest); |
| 246 | |
| 247 | if (request & HIF_SYNCHRONOUS) { |
| 248 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: queued sync req: 0x%lX\n", (unsigned long)pReqPriv->busrequest)); |
| 249 | /* signal thread and wait */ |
| 250 | up(&device->sem_async); |
| 251 | if (down_interruptible(&pReqPriv->busrequest->sem_req) != 0) { |
| 252 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,("HIF-SCATTER: interrupted! \n")); |
| 253 | /* interrupted, exit */ |
| 254 | status = A_ERROR; |
| 255 | break; |
| 256 | } else { |
| 257 | status = pReq->CompletionStatus; |
| 258 | } |
| 259 | } else { |
| 260 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: queued async req: 0x%lX\n", (unsigned long)pReqPriv->busrequest)); |
| 261 | /* wake thread, it will process and then take care of the async callback */ |
| 262 | up(&device->sem_async); |
| 263 | status = A_OK; |
| 264 | } |
| 265 | |
Joe Perches | 1071a13 | 2011-02-02 14:05:47 -0800 | [diff] [blame] | 266 | } while (false); |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 267 | |
Joe Perches | 391bb21 | 2011-01-27 20:04:21 -0800 | [diff] [blame] | 268 | if (status && (request & HIF_ASYNCHRONOUS)) { |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 269 | pReq->CompletionStatus = status; |
| 270 | pReq->CompletionRoutine(pReq); |
| 271 | status = A_OK; |
| 272 | } |
| 273 | |
| 274 | return status; |
| 275 | } |
| 276 | |
| 277 | /* setup of HIF scatter resources */ |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 278 | int SetupHIFScatterSupport(HIF_DEVICE *device, HIF_DEVICE_SCATTER_SUPPORT_INFO *pInfo) |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 279 | { |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 280 | int status = A_ERROR; |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 281 | int i; |
| 282 | HIF_SCATTER_REQ_PRIV *pReqPriv; |
| 283 | BUS_REQUEST *busrequest; |
| 284 | |
| 285 | do { |
| 286 | |
| 287 | /* check if host supports scatter requests and it meets our requirements */ |
Vipin Mehta | 73bb2f2 | 2010-09-17 18:45:38 -0700 | [diff] [blame] | 288 | if (device->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) { |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 289 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HIF-SCATTER : host only supports scatter of : %d entries, need: %d \n", |
Vipin Mehta | 73bb2f2 | 2010-09-17 18:45:38 -0700 | [diff] [blame] | 290 | device->func->card->host->max_segs, MAX_SCATTER_ENTRIES_PER_REQ)); |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 291 | status = A_ENOTSUP; |
| 292 | break; |
| 293 | } |
| 294 | |
| 295 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HIF-SCATTER Enabled: max scatter req : %d entries: %d \n", |
| 296 | MAX_SCATTER_REQUESTS, MAX_SCATTER_ENTRIES_PER_REQ)); |
| 297 | |
| 298 | for (i = 0; i < MAX_SCATTER_REQUESTS; i++) { |
| 299 | /* allocate the private request blob */ |
| 300 | pReqPriv = (HIF_SCATTER_REQ_PRIV *)A_MALLOC(sizeof(HIF_SCATTER_REQ_PRIV)); |
| 301 | if (NULL == pReqPriv) { |
| 302 | break; |
| 303 | } |
| 304 | A_MEMZERO(pReqPriv, sizeof(HIF_SCATTER_REQ_PRIV)); |
| 305 | /* save the device instance*/ |
| 306 | pReqPriv->device = device; |
| 307 | /* allocate the scatter request */ |
| 308 | pReqPriv->pHifScatterReq = (HIF_SCATTER_REQ *)A_MALLOC(sizeof(HIF_SCATTER_REQ) + |
| 309 | (MAX_SCATTER_ENTRIES_PER_REQ - 1) * (sizeof(HIF_SCATTER_ITEM))); |
| 310 | |
| 311 | if (NULL == pReqPriv->pHifScatterReq) { |
| 312 | A_FREE(pReqPriv); |
| 313 | break; |
| 314 | } |
| 315 | /* just zero the main part of the scatter request */ |
| 316 | A_MEMZERO(pReqPriv->pHifScatterReq, sizeof(HIF_SCATTER_REQ)); |
| 317 | /* back pointer to the private struct */ |
| 318 | pReqPriv->pHifScatterReq->HIFPrivate[0] = pReqPriv; |
| 319 | /* allocate a bus request for this scatter request */ |
| 320 | busrequest = hifAllocateBusRequest(device); |
| 321 | if (NULL == busrequest) { |
| 322 | A_FREE(pReqPriv->pHifScatterReq); |
| 323 | A_FREE(pReqPriv); |
| 324 | break; |
| 325 | } |
| 326 | /* assign the scatter request to this bus request */ |
| 327 | busrequest->pScatterReq = pReqPriv; |
| 328 | /* point back to the request */ |
| 329 | pReqPriv->busrequest = busrequest; |
| 330 | /* add it to the scatter pool */ |
| 331 | FreeScatterReq(device,pReqPriv->pHifScatterReq); |
| 332 | } |
| 333 | |
| 334 | if (i != MAX_SCATTER_REQUESTS) { |
| 335 | status = A_NO_MEMORY; |
| 336 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HIF-SCATTER : failed to alloc scatter resources !\n")); |
| 337 | break; |
| 338 | } |
| 339 | |
| 340 | /* set scatter function pointers */ |
| 341 | pInfo->pAllocateReqFunc = AllocScatterReq; |
| 342 | pInfo->pFreeReqFunc = FreeScatterReq; |
| 343 | pInfo->pReadWriteScatterFunc = HifReadWriteScatter; |
| 344 | pInfo->MaxScatterEntries = MAX_SCATTER_ENTRIES_PER_REQ; |
| 345 | pInfo->MaxTransferSizePerScatterReq = MAX_SCATTER_REQ_TRANSFER_SIZE; |
| 346 | |
| 347 | status = A_OK; |
| 348 | |
Joe Perches | 1071a13 | 2011-02-02 14:05:47 -0800 | [diff] [blame] | 349 | } while (false); |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 350 | |
Joe Perches | 391bb21 | 2011-01-27 20:04:21 -0800 | [diff] [blame] | 351 | if (status) { |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 352 | CleanupHIFScatterResources(device); |
| 353 | } |
| 354 | |
| 355 | return status; |
| 356 | } |
| 357 | |
| 358 | /* clean up scatter support */ |
| 359 | void CleanupHIFScatterResources(HIF_DEVICE *device) |
| 360 | { |
| 361 | HIF_SCATTER_REQ_PRIV *pReqPriv; |
| 362 | HIF_SCATTER_REQ *pReq; |
| 363 | |
| 364 | /* empty the free list */ |
| 365 | |
| 366 | while (1) { |
| 367 | |
| 368 | pReq = AllocScatterReq(device); |
| 369 | |
| 370 | if (NULL == pReq) { |
| 371 | break; |
| 372 | } |
| 373 | |
| 374 | pReqPriv = (HIF_SCATTER_REQ_PRIV *)pReq->HIFPrivate[0]; |
| 375 | A_ASSERT(pReqPriv != NULL); |
| 376 | |
| 377 | if (pReqPriv->busrequest != NULL) { |
| 378 | pReqPriv->busrequest->pScatterReq = NULL; |
| 379 | /* free bus request */ |
| 380 | hifFreeBusRequest(device, pReqPriv->busrequest); |
| 381 | pReqPriv->busrequest = NULL; |
| 382 | } |
| 383 | |
| 384 | if (pReqPriv->pHifScatterReq != NULL) { |
| 385 | A_FREE(pReqPriv->pHifScatterReq); |
| 386 | pReqPriv->pHifScatterReq = NULL; |
| 387 | } |
| 388 | |
| 389 | A_FREE(pReqPriv); |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | #endif // HIF_LINUX_MMC_SCATTER_SUPPORT |