diff options
| author | Kukjin Kim <kgene.kim@samsung.com> | 2014-05-30 13:36:49 -0400 |
|---|---|---|
| committer | Kukjin Kim <kgene.kim@samsung.com> | 2014-05-30 13:36:49 -0400 |
| commit | fced6dee29f6fb143fe16ea90331176ff77e6120 (patch) | |
| tree | 5b6e57e7a757adc2a6518ce291a4d2914397b917 /drivers/misc/genwqe/card_utils.c | |
| parent | bfed1074f213051e94648bfad0d0611a16d81366 (diff) | |
| parent | be1f7c8d7e2bc8b8c76846aa6f276e8d2ef8975a (diff) | |
Merge branch 'v3.16-next/cleanup-samsung' into v3.16-next/platform-exynos
Diffstat (limited to 'drivers/misc/genwqe/card_utils.c')
| -rw-r--r-- | drivers/misc/genwqe/card_utils.c | 170 |
1 files changed, 130 insertions, 40 deletions
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 6b1a6ef9f1a8..d049d271699c 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c | |||
| @@ -275,67 +275,107 @@ static int genwqe_sgl_size(int num_pages) | |||
| 275 | return roundup(len, PAGE_SIZE); | 275 | return roundup(len, PAGE_SIZE); |
| 276 | } | 276 | } |
| 277 | 277 | ||
| 278 | struct sg_entry *genwqe_alloc_sgl(struct genwqe_dev *cd, int num_pages, | 278 | /** |
| 279 | dma_addr_t *dma_addr, size_t *sgl_size) | 279 | * genwqe_alloc_sync_sgl() - Allocate memory for sgl and overlapping pages |
| 280 | * | ||
| 281 | * Allocates memory for sgl and overlapping pages. Pages which might | ||
| 282 | * overlap other user-space memory blocks are being cached for DMAs, | ||
| 283 | * such that we do not run into syncronization issues. Data is copied | ||
| 284 | * from user-space into the cached pages. | ||
| 285 | */ | ||
| 286 | int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, | ||
| 287 | void __user *user_addr, size_t user_size) | ||
| 280 | { | 288 | { |
| 289 | int rc; | ||
| 281 | struct pci_dev *pci_dev = cd->pci_dev; | 290 | struct pci_dev *pci_dev = cd->pci_dev; |
| 282 | struct sg_entry *sgl; | ||
| 283 | 291 | ||
| 284 | *sgl_size = genwqe_sgl_size(num_pages); | 292 | sgl->fpage_offs = offset_in_page((unsigned long)user_addr); |
| 285 | if (get_order(*sgl_size) > MAX_ORDER) { | 293 | sgl->fpage_size = min_t(size_t, PAGE_SIZE-sgl->fpage_offs, user_size); |
| 294 | sgl->nr_pages = DIV_ROUND_UP(sgl->fpage_offs + user_size, PAGE_SIZE); | ||
| 295 | sgl->lpage_size = (user_size - sgl->fpage_size) % PAGE_SIZE; | ||
| 296 | |||
| 297 | dev_dbg(&pci_dev->dev, "[%s] uaddr=%p usize=%8ld nr_pages=%ld " | ||
| 298 | "fpage_offs=%lx fpage_size=%ld lpage_size=%ld\n", | ||
| 299 | __func__, user_addr, user_size, sgl->nr_pages, | ||
| 300 | sgl->fpage_offs, sgl->fpage_size, sgl->lpage_size); | ||
| 301 | |||
| 302 | sgl->user_addr = user_addr; | ||
| 303 | sgl->user_size = user_size; | ||
| 304 | sgl->sgl_size = genwqe_sgl_size(sgl->nr_pages); | ||
| 305 | |||
| 306 | if (get_order(sgl->sgl_size) > MAX_ORDER) { | ||
| 286 | dev_err(&pci_dev->dev, | 307 | dev_err(&pci_dev->dev, |
| 287 | "[%s] err: too much memory requested!\n", __func__); | 308 | "[%s] err: too much memory requested!\n", __func__); |
| 288 | return NULL; | 309 | return -ENOMEM; |
| 289 | } | 310 | } |
| 290 | 311 | ||
| 291 | sgl = __genwqe_alloc_consistent(cd, *sgl_size, dma_addr); | 312 | sgl->sgl = __genwqe_alloc_consistent(cd, sgl->sgl_size, |
| 292 | if (sgl == NULL) { | 313 | &sgl->sgl_dma_addr); |
| 314 | if (sgl->sgl == NULL) { | ||
| 293 | dev_err(&pci_dev->dev, | 315 | dev_err(&pci_dev->dev, |
| 294 | "[%s] err: no memory available!\n", __func__); | 316 | "[%s] err: no memory available!\n", __func__); |
| 295 | return NULL; | 317 | return -ENOMEM; |
| 296 | } | 318 | } |
| 297 | 319 | ||
| 298 | return sgl; | 320 | /* Only use buffering on incomplete pages */ |
| 321 | if ((sgl->fpage_size != 0) && (sgl->fpage_size != PAGE_SIZE)) { | ||
| 322 | sgl->fpage = __genwqe_alloc_consistent(cd, PAGE_SIZE, | ||
| 323 | &sgl->fpage_dma_addr); | ||
| 324 | if (sgl->fpage == NULL) | ||
| 325 | goto err_out; | ||
| 326 | |||
| 327 | /* Sync with user memory */ | ||
| 328 | if (copy_from_user(sgl->fpage + sgl->fpage_offs, | ||
| 329 | user_addr, sgl->fpage_size)) { | ||
| 330 | rc = -EFAULT; | ||
| 331 | goto err_out; | ||
| 332 | } | ||
| 333 | } | ||
| 334 | if (sgl->lpage_size != 0) { | ||
| 335 | sgl->lpage = __genwqe_alloc_consistent(cd, PAGE_SIZE, | ||
| 336 | &sgl->lpage_dma_addr); | ||
| 337 | if (sgl->lpage == NULL) | ||
| 338 | goto err_out1; | ||
| 339 | |||
| 340 | /* Sync with user memory */ | ||
| 341 | if (copy_from_user(sgl->lpage, user_addr + user_size - | ||
| 342 | sgl->lpage_size, sgl->lpage_size)) { | ||
| 343 | rc = -EFAULT; | ||
| 344 | goto err_out1; | ||
| 345 | } | ||
| 346 | } | ||
| 347 | return 0; | ||
| 348 | |||
| 349 | err_out1: | ||
| 350 | __genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage, | ||
| 351 | sgl->fpage_dma_addr); | ||
| 352 | err_out: | ||
| 353 | __genwqe_free_consistent(cd, sgl->sgl_size, sgl->sgl, | ||
| 354 | sgl->sgl_dma_addr); | ||
| 355 | return -ENOMEM; | ||
| 299 | } | 356 | } |
| 300 | 357 | ||
| 301 | int genwqe_setup_sgl(struct genwqe_dev *cd, | 358 | int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, |
| 302 | unsigned long offs, | 359 | dma_addr_t *dma_list) |
| 303 | unsigned long size, | ||
| 304 | struct sg_entry *sgl, | ||
| 305 | dma_addr_t dma_addr, size_t sgl_size, | ||
| 306 | dma_addr_t *dma_list, int page_offs, int num_pages) | ||
| 307 | { | 360 | { |
| 308 | int i = 0, j = 0, p; | 361 | int i = 0, j = 0, p; |
| 309 | unsigned long dma_offs, map_offs; | 362 | unsigned long dma_offs, map_offs; |
| 310 | struct pci_dev *pci_dev = cd->pci_dev; | ||
| 311 | dma_addr_t prev_daddr = 0; | 363 | dma_addr_t prev_daddr = 0; |
| 312 | struct sg_entry *s, *last_s = NULL; | 364 | struct sg_entry *s, *last_s = NULL; |
| 313 | 365 | size_t size = sgl->user_size; | |
| 314 | /* sanity checks */ | ||
| 315 | if (offs > PAGE_SIZE) { | ||
| 316 | dev_err(&pci_dev->dev, | ||
| 317 | "[%s] too large start offs %08lx\n", __func__, offs); | ||
| 318 | return -EFAULT; | ||
| 319 | } | ||
| 320 | if (sgl_size < genwqe_sgl_size(num_pages)) { | ||
| 321 | dev_err(&pci_dev->dev, | ||
| 322 | "[%s] sgl_size too small %08lx for %d pages\n", | ||
| 323 | __func__, sgl_size, num_pages); | ||
| 324 | return -EFAULT; | ||
| 325 | } | ||
| 326 | 366 | ||
| 327 | dma_offs = 128; /* next block if needed/dma_offset */ | 367 | dma_offs = 128; /* next block if needed/dma_offset */ |
| 328 | map_offs = offs; /* offset in first page */ | 368 | map_offs = sgl->fpage_offs; /* offset in first page */ |
| 329 | 369 | ||
| 330 | s = &sgl[0]; /* first set of 8 entries */ | 370 | s = &sgl->sgl[0]; /* first set of 8 entries */ |
| 331 | p = 0; /* page */ | 371 | p = 0; /* page */ |
| 332 | while (p < num_pages) { | 372 | while (p < sgl->nr_pages) { |
| 333 | dma_addr_t daddr; | 373 | dma_addr_t daddr; |
| 334 | unsigned int size_to_map; | 374 | unsigned int size_to_map; |
| 335 | 375 | ||
| 336 | /* always write the chaining entry, cleanup is done later */ | 376 | /* always write the chaining entry, cleanup is done later */ |
| 337 | j = 0; | 377 | j = 0; |
| 338 | s[j].target_addr = cpu_to_be64(dma_addr + dma_offs); | 378 | s[j].target_addr = cpu_to_be64(sgl->sgl_dma_addr + dma_offs); |
| 339 | s[j].len = cpu_to_be32(128); | 379 | s[j].len = cpu_to_be32(128); |
| 340 | s[j].flags = cpu_to_be32(SG_CHAINED); | 380 | s[j].flags = cpu_to_be32(SG_CHAINED); |
| 341 | j++; | 381 | j++; |
| @@ -343,7 +383,17 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, | |||
| 343 | while (j < 8) { | 383 | while (j < 8) { |
| 344 | /* DMA mapping for requested page, offs, size */ | 384 | /* DMA mapping for requested page, offs, size */ |
| 345 | size_to_map = min(size, PAGE_SIZE - map_offs); | 385 | size_to_map = min(size, PAGE_SIZE - map_offs); |
| 346 | daddr = dma_list[page_offs + p] + map_offs; | 386 | |
| 387 | if ((p == 0) && (sgl->fpage != NULL)) { | ||
| 388 | daddr = sgl->fpage_dma_addr + map_offs; | ||
| 389 | |||
| 390 | } else if ((p == sgl->nr_pages - 1) && | ||
| 391 | (sgl->lpage != NULL)) { | ||
| 392 | daddr = sgl->lpage_dma_addr; | ||
| 393 | } else { | ||
| 394 | daddr = dma_list[p] + map_offs; | ||
| 395 | } | ||
| 396 | |||
| 347 | size -= size_to_map; | 397 | size -= size_to_map; |
| 348 | map_offs = 0; | 398 | map_offs = 0; |
| 349 | 399 | ||
| @@ -358,7 +408,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, | |||
| 358 | size_to_map); | 408 | size_to_map); |
| 359 | 409 | ||
| 360 | p++; /* process next page */ | 410 | p++; /* process next page */ |
| 361 | if (p == num_pages) | 411 | if (p == sgl->nr_pages) |
| 362 | goto fixup; /* nothing to do */ | 412 | goto fixup; /* nothing to do */ |
| 363 | 413 | ||
| 364 | prev_daddr = daddr + size_to_map; | 414 | prev_daddr = daddr + size_to_map; |
| @@ -374,7 +424,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, | |||
| 374 | j++; | 424 | j++; |
| 375 | 425 | ||
| 376 | p++; /* process next page */ | 426 | p++; /* process next page */ |
| 377 | if (p == num_pages) | 427 | if (p == sgl->nr_pages) |
| 378 | goto fixup; /* nothing to do */ | 428 | goto fixup; /* nothing to do */ |
| 379 | } | 429 | } |
| 380 | dma_offs += 128; | 430 | dma_offs += 128; |
| @@ -395,10 +445,50 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, | |||
| 395 | return 0; | 445 | return 0; |
| 396 | } | 446 | } |
| 397 | 447 | ||
| 398 | void genwqe_free_sgl(struct genwqe_dev *cd, struct sg_entry *sg_list, | 448 | /** |
| 399 | dma_addr_t dma_addr, size_t size) | 449 | * genwqe_free_sync_sgl() - Free memory for sgl and overlapping pages |
| 450 | * | ||
| 451 | * After the DMA transfer has been completed we free the memory for | ||
| 452 | * the sgl and the cached pages. Data is being transfered from cached | ||
| 453 | * pages into user-space buffers. | ||
| 454 | */ | ||
| 455 | int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl) | ||
| 400 | { | 456 | { |
| 401 | __genwqe_free_consistent(cd, size, sg_list, dma_addr); | 457 | int rc; |
| 458 | struct pci_dev *pci_dev = cd->pci_dev; | ||
| 459 | |||
| 460 | if (sgl->fpage) { | ||
| 461 | if (copy_to_user(sgl->user_addr, sgl->fpage + sgl->fpage_offs, | ||
| 462 | sgl->fpage_size)) { | ||
| 463 | dev_err(&pci_dev->dev, "[%s] err: copying fpage!\n", | ||
| 464 | __func__); | ||
| 465 | rc = -EFAULT; | ||
| 466 | } | ||
| 467 | __genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage, | ||
| 468 | sgl->fpage_dma_addr); | ||
| 469 | sgl->fpage = NULL; | ||
| 470 | sgl->fpage_dma_addr = 0; | ||
| 471 | } | ||
| 472 | if (sgl->lpage) { | ||
| 473 | if (copy_to_user(sgl->user_addr + sgl->user_size - | ||
| 474 | sgl->lpage_size, sgl->lpage, | ||
| 475 | sgl->lpage_size)) { | ||
| 476 | dev_err(&pci_dev->dev, "[%s] err: copying lpage!\n", | ||
| 477 | __func__); | ||
| 478 | rc = -EFAULT; | ||
| 479 | } | ||
| 480 | __genwqe_free_consistent(cd, PAGE_SIZE, sgl->lpage, | ||
| 481 | sgl->lpage_dma_addr); | ||
| 482 | sgl->lpage = NULL; | ||
| 483 | sgl->lpage_dma_addr = 0; | ||
| 484 | } | ||
| 485 | __genwqe_free_consistent(cd, sgl->sgl_size, sgl->sgl, | ||
| 486 | sgl->sgl_dma_addr); | ||
| 487 | |||
| 488 | sgl->sgl = NULL; | ||
| 489 | sgl->sgl_dma_addr = 0x0; | ||
| 490 | sgl->sgl_size = 0; | ||
| 491 | return rc; | ||
| 402 | } | 492 | } |
| 403 | 493 | ||
| 404 | /** | 494 | /** |
