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(x: NAND_BBT_BLOCK_NUM_STATUS); |
26 | unsigned int nblocks = nanddev_neraseblocks(nand); |
27 | |
28 | nand->bbt.cache = bitmap_zalloc(nbits: nblocks * bits_per_block, GFP_KERNEL); |
29 | if (!nand->bbt.cache) |
30 | return -ENOMEM; |
31 | |
32 | return 0; |
33 | } |
34 | EXPORT_SYMBOL_GPL(nanddev_bbt_init); |
35 | |
36 | /** |
37 | * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table) |
38 | * @nand: NAND device |
39 | * |
40 | * Undoes what has been done in nanddev_bbt_init() |
41 | */ |
42 | void nanddev_bbt_cleanup(struct nand_device *nand) |
43 | { |
44 | bitmap_free(bitmap: nand->bbt.cache); |
45 | } |
46 | EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup); |
47 | |
48 | /** |
49 | * nanddev_bbt_update() - Update a BBT |
50 | * @nand: nand device |
51 | * |
52 | * Update the BBT. Currently a NOP function since on-flash bbt is not yet |
53 | * supported. |
54 | * |
55 | * Return: 0 in case of success, a negative error code otherwise. |
56 | */ |
57 | int nanddev_bbt_update(struct nand_device *nand) |
58 | { |
59 | return 0; |
60 | } |
61 | EXPORT_SYMBOL_GPL(nanddev_bbt_update); |
62 | |
63 | /** |
64 | * nanddev_bbt_get_block_status() - Return the status of an eraseblock |
65 | * @nand: nand device |
66 | * @entry: the BBT entry |
67 | * |
68 | * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry |
69 | * is bigger than the BBT size. |
70 | */ |
71 | int nanddev_bbt_get_block_status(const struct nand_device *nand, |
72 | unsigned int entry) |
73 | { |
74 | unsigned int bits_per_block = fls(x: NAND_BBT_BLOCK_NUM_STATUS); |
75 | unsigned long *pos = nand->bbt.cache + |
76 | ((entry * bits_per_block) / BITS_PER_LONG); |
77 | unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; |
78 | unsigned long status; |
79 | |
80 | if (entry >= nanddev_neraseblocks(nand)) |
81 | return -ERANGE; |
82 | |
83 | status = pos[0] >> offs; |
84 | if (bits_per_block + offs > BITS_PER_LONG) |
85 | status |= pos[1] << (BITS_PER_LONG - offs); |
86 | |
87 | return status & GENMASK(bits_per_block - 1, 0); |
88 | } |
89 | EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status); |
90 | |
91 | /** |
92 | * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the |
93 | * in-memory BBT |
94 | * @nand: nand device |
95 | * @entry: the BBT entry to update |
96 | * @status: the new status |
97 | * |
98 | * Update an entry of the in-memory BBT. If you want to push the updated BBT |
99 | * the NAND you should call nanddev_bbt_update(). |
100 | * |
101 | * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT |
102 | * size. |
103 | */ |
104 | int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, |
105 | enum nand_bbt_block_status status) |
106 | { |
107 | unsigned int bits_per_block = fls(x: NAND_BBT_BLOCK_NUM_STATUS); |
108 | unsigned long *pos = nand->bbt.cache + |
109 | ((entry * bits_per_block) / BITS_PER_LONG); |
110 | unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; |
111 | unsigned long val = status & GENMASK(bits_per_block - 1, 0); |
112 | |
113 | if (entry >= nanddev_neraseblocks(nand)) |
114 | return -ERANGE; |
115 | |
116 | pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs); |
117 | pos[0] |= val << offs; |
118 | |
119 | if (bits_per_block + offs > BITS_PER_LONG) { |
120 | unsigned int rbits = bits_per_block + offs - BITS_PER_LONG; |
121 | |
122 | pos[1] &= ~GENMASK(rbits - 1, 0); |
123 | pos[1] |= val >> (bits_per_block - rbits); |
124 | } |
125 | |
126 | return 0; |
127 | } |
128 | EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status); |
129 | |