blob: 133670cb19983698d511a1d136665024814d9f01 [file] [log] [blame]
Boris Brezillonb95db8d2018-08-16 17:30:09 +02001// 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
Miquel Raynalc8f555f2019-10-03 19:50:21 +020012#include <common.h>
Simon Glass61b29b82020-02-03 07:36:15 -070013#include <dm/devres.h>
Boris Brezillonb95db8d2018-08-16 17:30:09 +020014#include <linux/mtd/nand.h>
15#ifndef __UBOOT__
16#include <linux/slab.h>
17#endif
18
19/**
20 * nanddev_bbt_init() - Initialize the BBT (Bad Block Table)
21 * @nand: NAND device
22 *
23 * Initialize the in-memory BBT.
24 *
25 * Return: 0 in case of success, a negative error code otherwise.
26 */
27int nanddev_bbt_init(struct nand_device *nand)
28{
29 unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
30 unsigned int nblocks = nanddev_neraseblocks(nand);
31 unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
32 BITS_PER_LONG);
33
34 nand->bbt.cache = kzalloc(nwords, GFP_KERNEL);
35 if (!nand->bbt.cache)
36 return -ENOMEM;
37
38 return 0;
39}
40EXPORT_SYMBOL_GPL(nanddev_bbt_init);
41
42/**
43 * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table)
44 * @nand: NAND device
45 *
46 * Undoes what has been done in nanddev_bbt_init()
47 */
48void nanddev_bbt_cleanup(struct nand_device *nand)
49{
50 kfree(nand->bbt.cache);
51}
52EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup);
53
54/**
55 * nanddev_bbt_update() - Update a BBT
56 * @nand: nand device
57 *
58 * Update the BBT. Currently a NOP function since on-flash bbt is not yet
59 * supported.
60 *
61 * Return: 0 in case of success, a negative error code otherwise.
62 */
63int nanddev_bbt_update(struct nand_device *nand)
64{
65 return 0;
66}
67EXPORT_SYMBOL_GPL(nanddev_bbt_update);
68
69/**
70 * nanddev_bbt_get_block_status() - Return the status of an eraseblock
71 * @nand: nand device
72 * @entry: the BBT entry
73 *
74 * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry
75 * is bigger than the BBT size.
76 */
77int nanddev_bbt_get_block_status(const struct nand_device *nand,
78 unsigned int entry)
79{
80 unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
81 unsigned long *pos = nand->bbt.cache +
82 ((entry * bits_per_block) / BITS_PER_LONG);
83 unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
84 unsigned long status;
85
86 if (entry >= nanddev_neraseblocks(nand))
87 return -ERANGE;
88
89 status = pos[0] >> offs;
90 if (bits_per_block + offs > BITS_PER_LONG)
91 status |= pos[1] << (BITS_PER_LONG - offs);
92
93 return status & GENMASK(bits_per_block - 1, 0);
94}
95EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status);
96
97/**
98 * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the
99 * in-memory BBT
100 * @nand: nand device
101 * @entry: the BBT entry to update
102 * @status: the new status
103 *
104 * Update an entry of the in-memory BBT. If you want to push the updated BBT
105 * the NAND you should call nanddev_bbt_update().
106 *
107 * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT
108 * size.
109 */
110int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
111 enum nand_bbt_block_status status)
112{
113 unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
114 unsigned long *pos = nand->bbt.cache +
115 ((entry * bits_per_block) / BITS_PER_LONG);
116 unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
117 unsigned long val = status & GENMASK(bits_per_block - 1, 0);
118
119 if (entry >= nanddev_neraseblocks(nand))
120 return -ERANGE;
121
122 pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs);
123 pos[0] |= val << offs;
124
125 if (bits_per_block + offs > BITS_PER_LONG) {
126 unsigned int rbits = bits_per_block + offs - BITS_PER_LONG;
127
128 pos[1] &= ~GENMASK(rbits - 1, 0);
129 pos[1] |= val >> rbits;
130 }
131
132 return 0;
133}
134EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status);