aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/lightnvm
diff options
context:
space:
mode:
authorMatias Bjørling <m@bjorling.me>2016-01-12 01:49:35 -0500
committerJens Axboe <axboe@fb.com>2016-01-12 10:21:17 -0500
commitca5927e7ab5307965104ca58bbb29d110b1d4545 (patch)
treef64096e4c7fc250829fee2020089bf9835cd1d43 /drivers/lightnvm
parentf9a9995072904f2d67d649545f17f81e00f4985e (diff)
lightnvm: introduce mlc lower page table mappings
NAND MLC memories have both lower and upper pages. When programming, both of these must be written, before data can be read. However, these lower and upper pages might not placed at even and odd flash pages, but can be skipped. Therefore each flash memory has its lower pages defined, which can then be used when programming and to know when padding are necessary. This patch implements the lower page definition in the specification, and exposes it through a simple lookup table at dev->lptbl. Signed-off-by: Matias Bjørling <m@bjorling.me> Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'drivers/lightnvm')
-rw-r--r--drivers/lightnvm/core.c61
1 files changed, 60 insertions, 1 deletions
diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index 5199c12fead0..1f302cc73e0b 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -362,6 +362,51 @@ int nvm_submit_ppa(struct nvm_dev *dev, struct ppa_addr *ppa, int nr_ppas,
362} 362}
363EXPORT_SYMBOL(nvm_submit_ppa); 363EXPORT_SYMBOL(nvm_submit_ppa);
364 364
365static int nvm_init_slc_tbl(struct nvm_dev *dev, struct nvm_id_group *grp)
366{
367 int i;
368
369 dev->lps_per_blk = dev->pgs_per_blk;
370 dev->lptbl = kcalloc(dev->lps_per_blk, sizeof(int), GFP_KERNEL);
371 if (!dev->lptbl)
372 return -ENOMEM;
373
374 /* Just a linear array */
375 for (i = 0; i < dev->lps_per_blk; i++)
376 dev->lptbl[i] = i;
377
378 return 0;
379}
380
381static int nvm_init_mlc_tbl(struct nvm_dev *dev, struct nvm_id_group *grp)
382{
383 int i, p;
384 struct nvm_id_lp_mlc *mlc = &grp->lptbl.mlc;
385
386 if (!mlc->num_pairs)
387 return 0;
388
389 dev->lps_per_blk = mlc->num_pairs;
390 dev->lptbl = kcalloc(dev->lps_per_blk, sizeof(int), GFP_KERNEL);
391 if (!dev->lptbl)
392 return -ENOMEM;
393
394 /* The lower page table encoding consists of a list of bytes, where each
395 * has a lower and an upper half. The first half byte maintains the
396 * increment value and every value after is an offset added to the
397 * previous incrementation value */
398 dev->lptbl[0] = mlc->pairs[0] & 0xF;
399 for (i = 1; i < dev->lps_per_blk; i++) {
400 p = mlc->pairs[i >> 1];
401 if (i & 0x1) /* upper */
402 dev->lptbl[i] = dev->lptbl[i - 1] + ((p & 0xF0) >> 4);
403 else /* lower */
404 dev->lptbl[i] = dev->lptbl[i - 1] + (p & 0xF);
405 }
406
407 return 0;
408}
409
365static int nvm_core_init(struct nvm_dev *dev) 410static int nvm_core_init(struct nvm_dev *dev)
366{ 411{
367 struct nvm_id *id = &dev->identity; 412 struct nvm_id *id = &dev->identity;
@@ -387,11 +432,23 @@ static int nvm_core_init(struct nvm_dev *dev)
387 return -EINVAL; 432 return -EINVAL;
388 } 433 }
389 434
390 if (grp->fmtype != 0 && grp->fmtype != 1) { 435 switch (grp->fmtype) {
436 case NVM_ID_FMTYPE_SLC:
437 if (nvm_init_slc_tbl(dev, grp))
438 return -ENOMEM;
439 break;
440 case NVM_ID_FMTYPE_MLC:
441 if (nvm_init_mlc_tbl(dev, grp))
442 return -ENOMEM;
443 break;
444 default:
391 pr_err("nvm: flash type not supported\n"); 445 pr_err("nvm: flash type not supported\n");
392 return -EINVAL; 446 return -EINVAL;
393 } 447 }
394 448
449 if (!dev->lps_per_blk)
450 pr_info("nvm: lower page programming table missing\n");
451
395 if (grp->mpos & 0x020202) 452 if (grp->mpos & 0x020202)
396 dev->plane_mode = NVM_PLANE_DOUBLE; 453 dev->plane_mode = NVM_PLANE_DOUBLE;
397 if (grp->mpos & 0x040404) 454 if (grp->mpos & 0x040404)
@@ -420,6 +477,8 @@ static void nvm_free(struct nvm_dev *dev)
420 477
421 if (dev->mt) 478 if (dev->mt)
422 dev->mt->unregister_mgr(dev); 479 dev->mt->unregister_mgr(dev);
480
481 kfree(dev->lptbl);
423} 482}
424 483
425static int nvm_init(struct nvm_dev *dev) 484static int nvm_init(struct nvm_dev *dev)