1235537Sgber/*-
2235537Sgber * Copyright (c) 2009-2012 Semihalf
3235537Sgber * All rights reserved.
4235537Sgber *
5235537Sgber * Redistribution and use in source and binary forms, with or without
6235537Sgber * modification, are permitted provided that the following conditions
7235537Sgber * are met:
8235537Sgber * 1. Redistributions of source code must retain the above copyright
9235537Sgber *    notice, this list of conditions and the following disclaimer.
10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
11235537Sgber *    notice, this list of conditions and the following disclaimer in the
12235537Sgber *    documentation and/or other materials provided with the distribution.
13235537Sgber *
14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17235537Sgber * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24235537Sgber * SUCH DAMAGE.
25235537Sgber *
26235537Sgber * $FreeBSD$
27235537Sgber */
28235537Sgber
29235537Sgber#include <sys/cdefs.h>
30235537Sgber
31235537Sgber#include <sys/param.h>
32235537Sgber#include <sys/systm.h>
33235537Sgber#include <sys/kernel.h>
34235537Sgber#include <sys/socket.h>
35235537Sgber#include <sys/malloc.h>
36235537Sgber#include <sys/bus.h>
37235537Sgber
38235537Sgber#include <dev/nand/nand.h>
39235537Sgber
40235537Sgber#include "nand_if.h"
41235537Sgber
42235537Sgber#define BBT_PRIMARY_PATTERN	0x01020304
43235537Sgber#define BBT_SECONDARY_PATTERN	0x05060708
44235537Sgber
45235537Sgberenum bbt_place {
46235537Sgber	BBT_NONE,
47235537Sgber	BBT_PRIMARY,
48235537Sgber	BBT_SECONDARY
49235537Sgber};
50235537Sgber
51235537Sgberstruct nand_bbt {
52235537Sgber	struct nand_chip	*chip;
53235537Sgber	uint32_t		primary_map;
54235537Sgber	uint32_t		secondary_map;
55235537Sgber	enum bbt_place		active;
56235537Sgber	struct bbt_header	*hdr;
57235537Sgber	uint32_t		tab_len;
58235537Sgber	uint32_t		*table;
59235537Sgber};
60235537Sgber
61235537Sgberstruct bbt_header {
62235537Sgber	uint32_t pattern;
63235537Sgber	int32_t seq_nr;
64235537Sgber};
65235537Sgber
66235537Sgberstatic int nand_bbt_save(struct nand_bbt *);
67235537Sgberstatic int nand_bbt_load_hdr(struct nand_bbt *, struct bbt_header *, int8_t);
68235537Sgberstatic int nand_bbt_load_table(struct nand_bbt *);
69235537Sgberstatic int nand_bbt_prescan(struct nand_bbt *);
70235537Sgber
71235537Sgberint
72235537Sgbernand_init_bbt(struct nand_chip *chip)
73235537Sgber{
74235537Sgber	struct chip_geom *cg;
75235537Sgber	struct nand_bbt *bbt;
76235537Sgber	int err;
77235537Sgber
78235537Sgber	cg = &chip->chip_geom;
79235537Sgber
80235537Sgber	bbt = malloc(sizeof(struct nand_bbt), M_NAND, M_ZERO | M_WAITOK);
81235537Sgber	if (!bbt) {
82235537Sgber		device_printf(chip->dev,
83235537Sgber		    "Cannot allocate memory for bad block struct");
84235537Sgber		return (ENOMEM);
85235537Sgber	}
86235537Sgber
87235537Sgber	bbt->chip = chip;
88235537Sgber	bbt->active = BBT_NONE;
89235537Sgber	bbt->primary_map = cg->chip_size - cg->block_size;
90235537Sgber	bbt->secondary_map = cg->chip_size - 2 * cg->block_size;
91235537Sgber	bbt->tab_len = cg->blks_per_chip * sizeof(uint32_t);
92235537Sgber	bbt->hdr = malloc(sizeof(struct bbt_header) + bbt->tab_len, M_NAND,
93235537Sgber	    M_WAITOK);
94235537Sgber	if (!bbt->hdr) {
95235537Sgber		device_printf(chip->dev, "Cannot allocate %d bytes for BB "
96235537Sgber		    "Table", bbt->tab_len);
97235537Sgber		free(bbt, M_NAND);
98235537Sgber		return (ENOMEM);
99235537Sgber	}
100235537Sgber	bbt->hdr->seq_nr = 0;
101235537Sgber	bbt->table = (uint32_t *)((uint8_t *)bbt->hdr +
102235537Sgber	    sizeof(struct bbt_header));
103235537Sgber
104235537Sgber	err = nand_bbt_load_table(bbt);
105235537Sgber	if (err) {
106235537Sgber		free(bbt->table, M_NAND);
107235537Sgber		free(bbt, M_NAND);
108235537Sgber		return (err);
109235537Sgber	}
110235537Sgber
111235537Sgber	chip->bbt = bbt;
112235537Sgber	if (bbt->active == BBT_NONE) {
113235537Sgber		bbt->active = BBT_PRIMARY;
114235537Sgber		memset(bbt->table, 0xff, bbt->tab_len);
115235537Sgber		nand_bbt_prescan(bbt);
116235537Sgber		nand_bbt_save(bbt);
117235537Sgber	} else
118235537Sgber		device_printf(chip->dev, "Found BBT table for chip\n");
119235537Sgber
120235537Sgber	return (0);
121235537Sgber}
122235537Sgber
123235537Sgbervoid
124235537Sgbernand_destroy_bbt(struct nand_chip *chip)
125235537Sgber{
126235537Sgber
127235537Sgber	if (chip->bbt) {
128235537Sgber		nand_bbt_save(chip->bbt);
129235537Sgber
130235537Sgber		free(chip->bbt->hdr, M_NAND);
131235537Sgber		free(chip->bbt, M_NAND);
132235537Sgber		chip->bbt = NULL;
133235537Sgber	}
134235537Sgber}
135235537Sgber
136235537Sgberint
137235537Sgbernand_update_bbt(struct nand_chip *chip)
138235537Sgber{
139235537Sgber
140235537Sgber	nand_bbt_save(chip->bbt);
141235537Sgber
142235537Sgber	return (0);
143235537Sgber}
144235537Sgber
145235537Sgberstatic int
146235537Sgbernand_bbt_save(struct nand_bbt *bbt)
147235537Sgber{
148235537Sgber	enum bbt_place next;
149235537Sgber	uint32_t addr;
150235537Sgber	int32_t err;
151235537Sgber
152235537Sgber	if (bbt->active == BBT_PRIMARY) {
153235537Sgber		addr = bbt->secondary_map;
154235537Sgber		bbt->hdr->pattern = BBT_SECONDARY_PATTERN;
155235537Sgber		next = BBT_SECONDARY;
156235537Sgber	} else {
157235537Sgber		addr = bbt->primary_map;
158235537Sgber		bbt->hdr->pattern = BBT_PRIMARY_PATTERN;
159235537Sgber		next = BBT_PRIMARY;
160235537Sgber	}
161235537Sgber
162235537Sgber	err = nand_erase_blocks(bbt->chip, addr,
163235537Sgber	    bbt->chip->chip_geom.block_size);
164235537Sgber	if (err)
165235537Sgber		return (err);
166235537Sgber
167235537Sgber	bbt->hdr->seq_nr++;
168235537Sgber
169235537Sgber	err = nand_prog_pages_raw(bbt->chip, addr, bbt->hdr,
170235537Sgber	    bbt->tab_len + sizeof(struct bbt_header));
171235537Sgber	if (err)
172235537Sgber		return (err);
173235537Sgber
174235537Sgber	bbt->active = next;
175235537Sgber	return (0);
176235537Sgber}
177235537Sgber
178235537Sgberstatic int
179235537Sgbernand_bbt_load_hdr(struct nand_bbt *bbt, struct bbt_header *hdr, int8_t primary)
180235537Sgber{
181235537Sgber	uint32_t addr;
182235537Sgber
183235537Sgber	if (primary)
184235537Sgber		addr = bbt->primary_map;
185235537Sgber	else
186235537Sgber		addr = bbt->secondary_map;
187235537Sgber
188235537Sgber	return (nand_read_pages_raw(bbt->chip, addr, hdr,
189235537Sgber	    sizeof(struct bbt_header)));
190235537Sgber}
191235537Sgber
192235537Sgberstatic int
193235537Sgbernand_bbt_load_table(struct nand_bbt *bbt)
194235537Sgber{
195235537Sgber	struct bbt_header hdr1, hdr2;
196235537Sgber	uint32_t address = 0;
197235537Sgber	int err = 0;
198235537Sgber
199235537Sgber	bzero(&hdr1, sizeof(hdr1));
200235537Sgber	bzero(&hdr2, sizeof(hdr2));
201235537Sgber
202235537Sgber	nand_bbt_load_hdr(bbt, &hdr1, 1);
203235537Sgber	if (hdr1.pattern == BBT_PRIMARY_PATTERN) {
204235537Sgber		bbt->active = BBT_PRIMARY;
205235537Sgber		address = bbt->primary_map;
206235537Sgber	} else
207235537Sgber		bzero(&hdr1, sizeof(hdr1));
208235537Sgber
209235537Sgber
210235537Sgber	nand_bbt_load_hdr(bbt, &hdr2, 0);
211235537Sgber	if ((hdr2.pattern == BBT_SECONDARY_PATTERN) &&
212235537Sgber	    (hdr2.seq_nr > hdr1.seq_nr)) {
213235537Sgber		bbt->active = BBT_SECONDARY;
214235537Sgber		address = bbt->secondary_map;
215235537Sgber	} else
216235537Sgber		bzero(&hdr2, sizeof(hdr2));
217235537Sgber
218235537Sgber	if (bbt->active != BBT_NONE)
219235537Sgber		err = nand_read_pages_raw(bbt->chip, address, bbt->hdr,
220235537Sgber		    bbt->tab_len + sizeof(struct bbt_header));
221235537Sgber
222235537Sgber	return (err);
223235537Sgber}
224235537Sgber
225235537Sgberstatic int
226235537Sgbernand_bbt_prescan(struct nand_bbt *bbt)
227235537Sgber{
228235537Sgber	int32_t i;
229235537Sgber	uint8_t bad;
230235537Sgber	bool printed_hash = 0;
231235537Sgber
232235537Sgber	device_printf(bbt->chip->dev, "No BBT found. Prescan chip...\n");
233235537Sgber	for (i = 0; i < bbt->chip->chip_geom.blks_per_chip; i++) {
234235537Sgber		if (NAND_IS_BLK_BAD(bbt->chip->dev, i, &bad))
235235537Sgber			return (ENXIO);
236235537Sgber
237235537Sgber		if (bad) {
238235537Sgber			device_printf(bbt->chip->dev, "Bad block(%d)\n", i);
239235537Sgber			bbt->table[i] = 0x0FFFFFFF;
240235537Sgber		}
241235537Sgber		if (!(i % 100)) {
242235537Sgber			printf("#");
243235537Sgber			printed_hash = 1;
244235537Sgber		}
245235537Sgber	}
246235537Sgber
247235537Sgber	if (printed_hash)
248235537Sgber		printf("\n");
249235537Sgber
250235537Sgber	return (0);
251235537Sgber}
252235537Sgber
253235537Sgberint
254235537Sgbernand_check_bad_block(struct nand_chip *chip, uint32_t block_number)
255235537Sgber{
256235537Sgber
257235537Sgber	if (!chip || !chip->bbt)
258235537Sgber		return (0);
259235537Sgber
260235537Sgber	if ((chip->bbt->table[block_number] & 0xF0000000) == 0)
261235537Sgber		return (1);
262235537Sgber
263235537Sgber	return (0);
264235537Sgber}
265235537Sgber
266235537Sgberint
267235537Sgbernand_mark_bad_block(struct nand_chip *chip, uint32_t block_number)
268235537Sgber{
269235537Sgber
270235537Sgber	chip->bbt->table[block_number] = 0x0FFFFFFF;
271235537Sgber
272235537Sgber	return (0);
273235537Sgber}
274