]>
Commit | Line | Data |
---|---|---|
9c3736a3 BB |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2017 Free Electrons | |
4 | * | |
5 | * Authors: | |
6 | * Boris Brezillon <boris.brezillon@free-electrons.com> | |
7 | * Peter Pan <peterpandong@micron.com> | |
8 | */ | |
9 | ||
10 | #define pr_fmt(fmt) "nand-bbt: " fmt | |
11 | ||
12 | #include <linux/mtd/nand.h> | |
13 | #include <linux/slab.h> | |
14 | ||
15 | /** | |
16 | * nanddev_bbt_init() - Initialize the BBT (Bad Block Table) | |
17 | * @nand: NAND device | |
18 | * | |
19 | * Initialize the in-memory BBT. | |
20 | * | |
21 | * Return: 0 in case of success, a negative error code otherwise. | |
22 | */ | |
23 | int nanddev_bbt_init(struct nand_device *nand) | |
24 | { | |
25 | unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); | |
26 | unsigned int nblocks = nanddev_neraseblocks(nand); | |
27 | unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block, | |
28 | BITS_PER_LONG); | |
29 | ||
40b41289 FS |
30 | nand->bbt.cache = kcalloc(nwords, sizeof(*nand->bbt.cache), |
31 | GFP_KERNEL); | |
9c3736a3 BB |
32 | if (!nand->bbt.cache) |
33 | return -ENOMEM; | |
34 | ||
35 | return 0; | |
36 | } | |
37 | EXPORT_SYMBOL_GPL(nanddev_bbt_init); | |
38 | ||
39 | /** | |
40 | * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table) | |
41 | * @nand: NAND device | |
42 | * | |
43 | * Undoes what has been done in nanddev_bbt_init() | |
44 | */ | |
45 | void nanddev_bbt_cleanup(struct nand_device *nand) | |
46 | { | |
47 | kfree(nand->bbt.cache); | |
48 | } | |
49 | EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup); | |
50 | ||
51 | /** | |
52 | * nanddev_bbt_update() - Update a BBT | |
53 | * @nand: nand device | |
54 | * | |
55 | * Update the BBT. Currently a NOP function since on-flash bbt is not yet | |
56 | * supported. | |
57 | * | |
58 | * Return: 0 in case of success, a negative error code otherwise. | |
59 | */ | |
60 | int nanddev_bbt_update(struct nand_device *nand) | |
61 | { | |
62 | return 0; | |
63 | } | |
64 | EXPORT_SYMBOL_GPL(nanddev_bbt_update); | |
65 | ||
66 | /** | |
67 | * nanddev_bbt_get_block_status() - Return the status of an eraseblock | |
68 | * @nand: nand device | |
69 | * @entry: the BBT entry | |
70 | * | |
71 | * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry | |
72 | * is bigger than the BBT size. | |
73 | */ | |
74 | int nanddev_bbt_get_block_status(const struct nand_device *nand, | |
75 | unsigned int entry) | |
76 | { | |
77 | unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); | |
78 | unsigned long *pos = nand->bbt.cache + | |
79 | ((entry * bits_per_block) / BITS_PER_LONG); | |
80 | unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; | |
81 | unsigned long status; | |
82 | ||
83 | if (entry >= nanddev_neraseblocks(nand)) | |
84 | return -ERANGE; | |
85 | ||
86 | status = pos[0] >> offs; | |
87 | if (bits_per_block + offs > BITS_PER_LONG) | |
88 | status |= pos[1] << (BITS_PER_LONG - offs); | |
89 | ||
90 | return status & GENMASK(bits_per_block - 1, 0); | |
91 | } | |
92 | EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status); | |
93 | ||
94 | /** | |
95 | * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the | |
96 | * in-memory BBT | |
97 | * @nand: nand device | |
98 | * @entry: the BBT entry to update | |
99 | * @status: the new status | |
100 | * | |
101 | * Update an entry of the in-memory BBT. If you want to push the updated BBT | |
102 | * the NAND you should call nanddev_bbt_update(). | |
103 | * | |
104 | * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT | |
105 | * size. | |
106 | */ | |
107 | int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, | |
108 | enum nand_bbt_block_status status) | |
109 | { | |
110 | unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); | |
111 | unsigned long *pos = nand->bbt.cache + | |
112 | ((entry * bits_per_block) / BITS_PER_LONG); | |
113 | unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; | |
114 | unsigned long val = status & GENMASK(bits_per_block - 1, 0); | |
115 | ||
116 | if (entry >= nanddev_neraseblocks(nand)) | |
117 | return -ERANGE; | |
118 | ||
119 | pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs); | |
120 | pos[0] |= val << offs; | |
121 | ||
122 | if (bits_per_block + offs > BITS_PER_LONG) { | |
123 | unsigned int rbits = bits_per_block + offs - BITS_PER_LONG; | |
124 | ||
125 | pos[1] &= ~GENMASK(rbits - 1, 0); | |
126 | pos[1] |= val >> rbits; | |
127 | } | |
128 | ||
129 | return 0; | |
130 | } | |
131 | EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status); |