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
27235537Sgber#include <sys/cdefs.h>
28235537Sgber__FBSDID("$FreeBSD$");
29235537Sgber
30235537Sgber#include <sys/param.h>
31235537Sgber#include <sys/systm.h>
32235537Sgber#include <sys/kernel.h>
33235537Sgber#include <sys/socket.h>
34235537Sgber#include <sys/malloc.h>
35235537Sgber#include <sys/module.h>
36235537Sgber#include <sys/bus.h>
37235537Sgber#include <sys/lock.h>
38235537Sgber#include <sys/mutex.h>
39235537Sgber#include <sys/callout.h>
40235537Sgber#include <sys/sysctl.h>
41235537Sgber
42235537Sgber#include <dev/nand/nand.h>
43235537Sgber#include <dev/nand/nandbus.h>
44235537Sgber#include <dev/nand/nand_ecc_pos.h>
45235537Sgber#include "nfc_if.h"
46235537Sgber#include "nand_if.h"
47235537Sgber#include "nandbus_if.h"
48235537Sgber#include <machine/stdarg.h>
49235537Sgber
50235537Sgber#define NAND_RESET_DELAY	1000	/* tRST */
51235537Sgber#define NAND_ERASE_DELAY	3000	/* tBERS */
52235537Sgber#define NAND_PROG_DELAY		700	/* tPROG */
53235537Sgber#define NAND_READ_DELAY		50	/* tR */
54235537Sgber
55235537Sgber#define BIT0(x) ((x) & 0x1)
56235537Sgber#define BIT1(x) (BIT0(x >> 1))
57235537Sgber#define BIT2(x) (BIT0(x >> 2))
58235537Sgber#define BIT3(x) (BIT0(x >> 3))
59235537Sgber#define BIT4(x) (BIT0(x >> 4))
60235537Sgber#define BIT5(x) (BIT0(x >> 5))
61235537Sgber#define BIT6(x) (BIT0(x >> 6))
62235537Sgber#define BIT7(x) (BIT0(x >> 7))
63235537Sgber
64235537Sgber#define	SOFTECC_SIZE		256
65235537Sgber#define	SOFTECC_BYTES		3
66235537Sgber
67235537Sgberint nand_debug_flag = 0;
68235537SgberSYSCTL_INT(_debug, OID_AUTO, nand_debug, CTLFLAG_RW, &nand_debug_flag, 0,
69235537Sgber    "NAND subsystem debug flag");
70235537Sgber
71235537Sgberstatic void
72235537Sgbernand_tunable_init(void *arg)
73235537Sgber{
74235537Sgber
75235537Sgber	TUNABLE_INT_FETCH("debug.nand", &nand_debug_flag);
76235537Sgber}
77235537Sgber
78235537SgberSYSINIT(nand_tunables, SI_SUB_VFS, SI_ORDER_ANY, nand_tunable_init, NULL);
79235537Sgber
80235537SgberMALLOC_DEFINE(M_NAND, "NAND", "NAND dynamic data");
81235537Sgber
82235537Sgberstatic void calculate_ecc(const uint8_t *, uint8_t *);
83235537Sgberstatic int correct_ecc(uint8_t *, uint8_t *, uint8_t *);
84235537Sgber
85235537Sgbervoid
86235537Sgbernand_debug(int level, const char *fmt, ...)
87235537Sgber{
88235537Sgber	va_list ap;
89235537Sgber
90235537Sgber	if (!(nand_debug_flag & level))
91235537Sgber		return;
92235537Sgber	va_start(ap, fmt);
93235537Sgber	vprintf(fmt, ap);
94235537Sgber	va_end(ap);
95235537Sgber	printf("\n");
96235537Sgber}
97235537Sgber
98235537Sgbervoid
99235537Sgbernand_init(struct nand_softc *nand, device_t dev, int ecc_mode,
100235537Sgber    int ecc_bytes, int ecc_size, uint16_t *eccposition, char *cdev_name)
101235537Sgber{
102235537Sgber
103235537Sgber	nand->ecc.eccmode = ecc_mode;
104235537Sgber	nand->chip_cdev_name = cdev_name;
105235537Sgber
106235537Sgber	if (ecc_mode == NAND_ECC_SOFT) {
107235537Sgber		nand->ecc.eccbytes = SOFTECC_BYTES;
108235537Sgber		nand->ecc.eccsize = SOFTECC_SIZE;
109235537Sgber	} else if (ecc_mode != NAND_ECC_NONE) {
110235537Sgber		nand->ecc.eccbytes = ecc_bytes;
111235537Sgber		nand->ecc.eccsize = ecc_size;
112235537Sgber		if (eccposition)
113235537Sgber			nand->ecc.eccpositions = eccposition;
114235537Sgber	}
115235537Sgber}
116235537Sgber
117235537Sgbervoid
118235537Sgbernand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params)
119235537Sgber{
120235537Sgber	struct chip_geom *cg;
121235537Sgber
122235537Sgber	cg = &chip->chip_geom;
123235537Sgber
124235537Sgber	init_chip_geom(cg, params->luns, params->blocks_per_lun,
125235537Sgber	    params->pages_per_block, params->bytes_per_page,
126235537Sgber	    params->spare_bytes_per_page);
127235537Sgber	chip->t_bers = params->t_bers;
128235537Sgber	chip->t_prog = params->t_prog;
129235537Sgber	chip->t_r = params->t_r;
130235537Sgber	chip->t_ccs = params->t_ccs;
131235537Sgber
132235537Sgber	if (params->features & ONFI_FEAT_16BIT)
133235537Sgber		chip->flags |= NAND_16_BIT;
134235537Sgber}
135235537Sgber
136235537Sgbervoid
137235537Sgbernand_set_params(struct nand_chip *chip, struct nand_params *params)
138235537Sgber{
139235537Sgber	struct chip_geom *cg;
140235537Sgber	uint32_t blocks_per_chip;
141235537Sgber
142235537Sgber	cg = &chip->chip_geom;
143235537Sgber	blocks_per_chip = (params->chip_size << 20) /
144235537Sgber	    (params->page_size * params->pages_per_block);
145235537Sgber
146235537Sgber	init_chip_geom(cg, 1, blocks_per_chip,
147235537Sgber	    params->pages_per_block, params->page_size,
148235537Sgber	    params->oob_size);
149235537Sgber
150235537Sgber	chip->t_bers = NAND_ERASE_DELAY;
151235537Sgber	chip->t_prog = NAND_PROG_DELAY;
152235537Sgber	chip->t_r = NAND_READ_DELAY;
153235537Sgber	chip->t_ccs = 0;
154235537Sgber
155235537Sgber	if (params->flags & NAND_16_BIT)
156235537Sgber		chip->flags |= NAND_16_BIT;
157235537Sgber}
158235537Sgber
159235537Sgberint
160235537Sgbernand_init_stat(struct nand_chip *chip)
161235537Sgber{
162235537Sgber	struct block_stat *blk_stat;
163235537Sgber	struct page_stat *pg_stat;
164235537Sgber	struct chip_geom *cg;
165235537Sgber	uint32_t blks, pgs;
166235537Sgber
167235537Sgber	cg = &chip->chip_geom;
168235537Sgber	blks = cg->blks_per_lun * cg->luns;
169235537Sgber	blk_stat = malloc(sizeof(struct block_stat) * blks, M_NAND,
170235537Sgber	    M_WAITOK | M_ZERO);
171235537Sgber	if (!blk_stat)
172235537Sgber		return (ENOMEM);
173235537Sgber
174235537Sgber	pgs = blks * cg->pgs_per_blk;
175235537Sgber	pg_stat = malloc(sizeof(struct page_stat) * pgs, M_NAND,
176235537Sgber	    M_WAITOK | M_ZERO);
177235537Sgber	if (!pg_stat) {
178235537Sgber		free(blk_stat, M_NAND);
179235537Sgber		return (ENOMEM);
180235537Sgber	}
181235537Sgber
182235537Sgber	chip->blk_stat = blk_stat;
183235537Sgber	chip->pg_stat = pg_stat;
184235537Sgber
185235537Sgber	return (0);
186235537Sgber}
187235537Sgber
188235537Sgbervoid
189235537Sgbernand_destroy_stat(struct nand_chip *chip)
190235537Sgber{
191235537Sgber
192235537Sgber	free(chip->pg_stat, M_NAND);
193235537Sgber	free(chip->blk_stat, M_NAND);
194235537Sgber}
195235537Sgber
196235537Sgberint
197235537Sgberinit_chip_geom(struct chip_geom *cg, uint32_t luns, uint32_t blks_per_lun,
198235537Sgber    uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size)
199235537Sgber{
200235537Sgber	int shift;
201235537Sgber
202235537Sgber	if (!cg)
203235537Sgber		return (-1);
204235537Sgber
205235537Sgber	cg->luns = luns;
206235537Sgber	cg->blks_per_lun = blks_per_lun;
207235537Sgber	cg->blks_per_chip = blks_per_lun * luns;
208235537Sgber	cg->pgs_per_blk = pgs_per_blk;
209235537Sgber
210235537Sgber	cg->page_size = pg_size;
211235537Sgber	cg->oob_size = oob_size;
212235537Sgber	cg->block_size = cg->page_size * cg->pgs_per_blk;
213235537Sgber	cg->chip_size = cg->block_size * cg->blks_per_chip;
214235537Sgber
215235537Sgber	shift = fls(cg->pgs_per_blk - 1);
216235537Sgber	cg->pg_mask = (1 << shift) - 1;
217235537Sgber	cg->blk_shift = shift;
218235537Sgber
219235537Sgber	if (cg->blks_per_lun > 0) {
220235537Sgber		shift = fls(cg->blks_per_lun - 1);
221235537Sgber		cg->blk_mask = ((1 << shift) - 1) << cg->blk_shift;
222235537Sgber	} else {
223235537Sgber		shift = 0;
224235537Sgber		cg->blk_mask = 0;
225235537Sgber	}
226235537Sgber
227235537Sgber	cg->lun_shift = shift + cg->blk_shift;
228235537Sgber	shift = fls(cg->luns - 1);
229235537Sgber	cg->lun_mask = ((1 << shift) - 1) << cg->lun_shift;
230235537Sgber
231235537Sgber	nand_debug(NDBG_NAND, "Masks: lun 0x%x blk 0x%x page 0x%x\n"
232235537Sgber	    "Shifts: lun %d blk %d",
233235537Sgber	    cg->lun_mask, cg->blk_mask, cg->pg_mask,
234235537Sgber	    cg->lun_shift, cg->blk_shift);
235235537Sgber
236235537Sgber	return (0);
237235537Sgber}
238235537Sgber
239235537Sgberint
240235537Sgbernand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun,
241235537Sgber    uint32_t *blk, uint32_t *pg)
242235537Sgber{
243235537Sgber
244235537Sgber	if (!cg || !lun || !blk || !pg)
245235537Sgber		return (-1);
246235537Sgber
247235537Sgber	if (row & ~(cg->lun_mask | cg->blk_mask | cg->pg_mask)) {
248235537Sgber		nand_debug(NDBG_NAND,"Address out of bounds\n");
249235537Sgber		return (-1);
250235537Sgber	}
251235537Sgber
252235537Sgber	*lun = (row & cg->lun_mask) >> cg->lun_shift;
253235537Sgber	*blk = (row & cg->blk_mask) >> cg->blk_shift;
254235537Sgber	*pg = (row & cg->pg_mask);
255235537Sgber
256235537Sgber	nand_debug(NDBG_NAND,"address %x-%x-%x\n", *lun, *blk, *pg);
257235537Sgber
258235537Sgber	return (0);
259235537Sgber}
260235537Sgber
261235537Sgberint page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row)
262235537Sgber{
263235537Sgber	uint32_t lun, block, pg_in_blk;
264235537Sgber
265235537Sgber	if (!cg || !row)
266235537Sgber		return (-1);
267235537Sgber
268235537Sgber	block = page / cg->pgs_per_blk;
269235537Sgber	pg_in_blk = page % cg->pgs_per_blk;
270235537Sgber
271235537Sgber	lun = block / cg->blks_per_lun;
272235537Sgber	block = block % cg->blks_per_lun;
273235537Sgber
274235537Sgber	*row = (lun << cg->lun_shift) & cg->lun_mask;
275235537Sgber	*row |= ((block << cg->blk_shift) & cg->blk_mask);
276235537Sgber	*row |= (pg_in_blk & cg->pg_mask);
277235537Sgber
278235537Sgber	return (0);
279235537Sgber}
280235537Sgber
281235537Sgberint
282235537Sgbernand_check_page_boundary(struct nand_chip *chip, uint32_t page)
283235537Sgber{
284235537Sgber	struct chip_geom* cg;
285235537Sgber
286235537Sgber	cg = &chip->chip_geom;
287235537Sgber	if (page >= (cg->pgs_per_blk * cg->blks_per_lun * cg->luns)) {
288235537Sgber		nand_debug(NDBG_GEN,"%s: page number too big %#x\n",
289235537Sgber		    __func__, page);
290235537Sgber		return (1);
291235537Sgber	}
292235537Sgber
293235537Sgber	return (0);
294235537Sgber}
295235537Sgber
296235537Sgbervoid
297235537Sgbernand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param)
298235537Sgber{
299235537Sgber	struct chip_geom *cg;
300235537Sgber
301235537Sgber	cg = &chip->chip_geom;
302235537Sgber	param->page_size = cg->page_size;
303235537Sgber	param->oob_size = cg->oob_size;
304235537Sgber
305235537Sgber	param->blocks = cg->blks_per_lun * cg->luns;
306235537Sgber	param->pages_per_block = cg->pgs_per_blk;
307235537Sgber}
308235537Sgber
309235537Sgberstatic uint16_t *
310235537Sgberdefault_software_ecc_positions(struct nand_chip *chip)
311235537Sgber{
312235537Sgber	struct nand_ecc_data *eccd;
313235537Sgber
314235537Sgber	eccd = &chip->nand->ecc;
315235537Sgber
316235537Sgber	if (eccd->eccpositions)
317235537Sgber		return (eccd->eccpositions);
318235537Sgber
319235537Sgber	switch (chip->chip_geom.oob_size) {
320235537Sgber	case 16:
321235537Sgber		return ((uint16_t *)&default_software_ecc_positions_16);
322235537Sgber	case 64:
323235537Sgber		return ((uint16_t *)&default_software_ecc_positions_64);
324235537Sgber	case 128:
325235537Sgber		return ((uint16_t *)&default_software_ecc_positions_128);
326235537Sgber	default:
327235537Sgber		return (NULL); /* No ecc bytes positions defs available */
328235537Sgber	}
329235537Sgber
330235537Sgber	return (NULL);
331235537Sgber}
332235537Sgber
333235537Sgberstatic void
334235537Sgbercalculate_ecc(const uint8_t *buf, uint8_t *ecc)
335235537Sgber{
336235537Sgber	uint8_t p8, byte;
337235537Sgber	int i;
338235537Sgber
339235537Sgber	memset(ecc, 0, 3);
340235537Sgber
341235537Sgber	for (i = 0; i < 256; i++) {
342235537Sgber		byte = buf[i];
343235537Sgber		ecc[0] ^= (BIT0(byte) ^ BIT2(byte) ^ BIT4(byte) ^
344235537Sgber		    BIT6(byte)) << 2;
345235537Sgber		ecc[0] ^= (BIT1(byte) ^ BIT3(byte) ^ BIT5(byte) ^
346235537Sgber		    BIT7(byte)) << 3;
347235537Sgber		ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT4(byte) ^
348235537Sgber		    BIT5(byte)) << 4;
349235537Sgber		ecc[0] ^= (BIT2(byte) ^ BIT3(byte) ^ BIT6(byte) ^
350235537Sgber		    BIT7(byte)) << 5;
351235537Sgber		ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
352235537Sgber		    BIT3(byte)) << 6;
353235537Sgber		ecc[0] ^= (BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
354235537Sgber		    BIT7(byte)) << 7;
355235537Sgber
356235537Sgber		p8 = BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
357235537Sgber		    BIT3(byte) ^ BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
358235537Sgber		    BIT7(byte);
359235537Sgber
360235537Sgber		if (p8) {
361235537Sgber			ecc[2] ^= (0x1 << BIT0(i));
362235537Sgber			ecc[2] ^= (0x4 << BIT1(i));
363235537Sgber			ecc[2] ^= (0x10 << BIT2(i));
364235537Sgber			ecc[2] ^= (0x40 << BIT3(i));
365235537Sgber
366235537Sgber			ecc[1] ^= (0x1 << BIT4(i));
367235537Sgber			ecc[1] ^= (0x4 << BIT5(i));
368235537Sgber			ecc[1] ^= (0x10 << BIT6(i));
369235537Sgber			ecc[1] ^= (0x40 << BIT7(i));
370235537Sgber		}
371235537Sgber	}
372235537Sgber	ecc[0] = ~ecc[0];
373235537Sgber	ecc[1] = ~ecc[1];
374235537Sgber	ecc[2] = ~ecc[2];
375235537Sgber	ecc[0] |= 3;
376235537Sgber}
377235537Sgber
378235537Sgberstatic int
379235537Sgbercorrect_ecc(uint8_t *buf, uint8_t *calc_ecc, uint8_t *read_ecc)
380235537Sgber{
381235537Sgber	uint8_t ecc0, ecc1, ecc2, onesnum, bit, byte;
382235537Sgber	uint16_t addr = 0;
383235537Sgber
384235537Sgber	ecc0 = calc_ecc[0] ^ read_ecc[0];
385235537Sgber	ecc1 = calc_ecc[1] ^ read_ecc[1];
386235537Sgber	ecc2 = calc_ecc[2] ^ read_ecc[2];
387235537Sgber
388235537Sgber	if (!ecc0 && !ecc1 && !ecc2)
389235537Sgber		return (ECC_OK);
390235537Sgber
391235537Sgber	addr = BIT3(ecc0) | (BIT5(ecc0) << 1) | (BIT7(ecc0) << 2);
392235537Sgber	addr |= (BIT1(ecc2) << 3) | (BIT3(ecc2) << 4) |
393235537Sgber	    (BIT5(ecc2) << 5) |  (BIT7(ecc2) << 6);
394235537Sgber	addr |= (BIT1(ecc1) << 7) | (BIT3(ecc1) << 8) |
395235537Sgber	    (BIT5(ecc1) << 9) |  (BIT7(ecc1) << 10);
396235537Sgber
397235537Sgber	onesnum = 0;
398235537Sgber	while (ecc0 || ecc1 || ecc2) {
399235537Sgber		if (ecc0 & 1)
400235537Sgber			onesnum++;
401235537Sgber		if (ecc1 & 1)
402235537Sgber			onesnum++;
403235537Sgber		if (ecc2 & 1)
404235537Sgber			onesnum++;
405235537Sgber
406235537Sgber		ecc0 >>= 1;
407235537Sgber		ecc1 >>= 1;
408235537Sgber		ecc2 >>= 1;
409235537Sgber	}
410235537Sgber
411235537Sgber	if (onesnum == 11) {
412235537Sgber		/* Correctable error */
413235537Sgber		bit = addr & 7;
414235537Sgber		byte = addr >> 3;
415235537Sgber		buf[byte] ^= (1 << bit);
416235537Sgber		return (ECC_CORRECTABLE);
417235537Sgber	} else if (onesnum == 1) {
418235537Sgber		/* ECC error */
419235537Sgber		return (ECC_ERROR_ECC);
420235537Sgber	} else {
421235537Sgber		/* Uncorrectable error */
422235537Sgber		return (ECC_UNCORRECTABLE);
423235537Sgber	}
424235537Sgber
425235537Sgber	return (0);
426235537Sgber}
427235537Sgber
428235537Sgberint
429235537Sgbernand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc)
430235537Sgber{
431235537Sgber	int steps = pagesize / SOFTECC_SIZE;
432235537Sgber	int i = 0, j = 0;
433235537Sgber
434235537Sgber	for (; i < (steps * SOFTECC_BYTES);
435235537Sgber	    i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
436235537Sgber		calculate_ecc(&buf[j], &ecc[i]);
437235537Sgber	}
438235537Sgber
439235537Sgber	return (0);
440235537Sgber}
441235537Sgber
442235537Sgberint
443235537Sgbernand_softecc_correct(device_t dev, uint8_t *buf, int pagesize,
444235537Sgber    uint8_t *readecc, uint8_t *calcecc)
445235537Sgber{
446235537Sgber	int steps = pagesize / SOFTECC_SIZE;
447235537Sgber	int i = 0, j = 0, ret = 0;
448235537Sgber
449235537Sgber	for (i = 0; i < (steps * SOFTECC_BYTES);
450235537Sgber	    i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
451235537Sgber		ret += correct_ecc(&buf[j], &calcecc[i], &readecc[i]);
452235537Sgber		if (ret < 0)
453235537Sgber			return (ret);
454235537Sgber	}
455235537Sgber
456235537Sgber	return (ret);
457235537Sgber}
458235537Sgber
459235537Sgberstatic int
460235537Sgberoffset_to_page(struct chip_geom *cg, uint32_t offset)
461235537Sgber{
462235537Sgber
463235537Sgber	return (offset / cg->page_size);
464235537Sgber}
465235537Sgber
466235537Sgberint
467235537Sgbernand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf,
468235537Sgber    uint32_t len)
469235537Sgber{
470235537Sgber	struct chip_geom *cg;
471235537Sgber	struct nand_ecc_data *eccd;
472235537Sgber	struct page_stat *pg_stat;
473235537Sgber	device_t nandbus;
474235537Sgber	void *oob = NULL;
475235537Sgber	uint8_t *ptr;
476235537Sgber	uint16_t *eccpos = NULL;
477235537Sgber	uint32_t page, num, steps = 0;
478235537Sgber	int i, retval = 0, needwrite;
479235537Sgber
480235537Sgber	nand_debug(NDBG_NAND,"%p read page %x[%x]", chip, offset, len);
481235537Sgber	cg = &chip->chip_geom;
482235537Sgber	eccd = &chip->nand->ecc;
483235537Sgber	page = offset_to_page(cg, offset);
484235537Sgber	num = len / cg->page_size;
485235537Sgber
486235537Sgber	if (eccd->eccmode != NAND_ECC_NONE) {
487235537Sgber		steps = cg->page_size / eccd->eccsize;
488235537Sgber		eccpos = default_software_ecc_positions(chip);
489235537Sgber		oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
490235537Sgber	}
491235537Sgber
492235537Sgber	nandbus = device_get_parent(chip->dev);
493235537Sgber	NANDBUS_LOCK(nandbus);
494235537Sgber	NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
495235537Sgber
496235537Sgber	ptr = (uint8_t *)buf;
497235537Sgber	while (num--) {
498235537Sgber		pg_stat = &(chip->pg_stat[page]);
499235537Sgber
500235537Sgber		if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
501235537Sgber			retval = ENXIO;
502235537Sgber			break;
503235537Sgber		}
504235537Sgber
505235537Sgber		if (eccd->eccmode != NAND_ECC_NONE) {
506235537Sgber			if (NAND_GET_ECC(chip->dev, ptr, eccd->ecccalculated,
507235537Sgber			    &needwrite)) {
508235537Sgber				retval = ENXIO;
509235537Sgber				break;
510235537Sgber			}
511235537Sgber			nand_debug(NDBG_ECC,"%s: ECC calculated:",
512235537Sgber			    __func__);
513235537Sgber			if (nand_debug_flag & NDBG_ECC)
514235537Sgber				for (i = 0; i < (eccd->eccbytes * steps); i++)
515235537Sgber					printf("%x ", eccd->ecccalculated[i]);
516235537Sgber
517235537Sgber			nand_debug(NDBG_ECC,"\n");
518235537Sgber
519235537Sgber			if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
520235537Sgber			    0)) {
521235537Sgber				retval = ENXIO;
522235537Sgber				break;
523235537Sgber			}
524235537Sgber			for (i = 0; i < (eccd->eccbytes * steps); i++)
525235537Sgber				eccd->eccread[i] = ((uint8_t *)oob)[eccpos[i]];
526235537Sgber
527235537Sgber			nand_debug(NDBG_ECC,"%s: ECC read:", __func__);
528235537Sgber			if (nand_debug_flag & NDBG_ECC)
529235537Sgber				for (i = 0; i < (eccd->eccbytes * steps); i++)
530235537Sgber					printf("%x ", eccd->eccread[i]);
531235537Sgber			nand_debug(NDBG_ECC,"\n");
532235537Sgber
533235537Sgber			retval = NAND_CORRECT_ECC(chip->dev, ptr, eccd->eccread,
534235537Sgber			    eccd->ecccalculated);
535235537Sgber
536235537Sgber			nand_debug(NDBG_ECC, "NAND_CORRECT_ECC() returned %d",
537235537Sgber			    retval);
538235537Sgber
539235537Sgber			if (retval == 0)
540235537Sgber				pg_stat->ecc_stat.ecc_succeded++;
541235537Sgber			else if (retval > 0) {
542235537Sgber				pg_stat->ecc_stat.ecc_corrected += retval;
543235537Sgber				retval = ECC_CORRECTABLE;
544235537Sgber			} else {
545235537Sgber				pg_stat->ecc_stat.ecc_failed++;
546235537Sgber				break;
547235537Sgber			}
548235537Sgber		}
549235537Sgber
550235537Sgber		pg_stat->page_read++;
551235537Sgber		page++;
552235537Sgber		ptr += cg->page_size;
553235537Sgber	}
554235537Sgber
555235537Sgber	NANDBUS_UNLOCK(nandbus);
556235537Sgber
557235537Sgber	if (oob)
558235537Sgber		free(oob, M_NAND);
559235537Sgber
560235537Sgber	return (retval);
561235537Sgber}
562235537Sgber
563235537Sgberint
564235537Sgbernand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
565235537Sgber    uint32_t len)
566235537Sgber{
567235537Sgber	struct chip_geom *cg;
568235537Sgber	device_t nandbus;
569235537Sgber	uint8_t *ptr;
570235537Sgber	uint32_t page, num, end, begin = 0, begin_off;
571235537Sgber	int retval = 0;
572235537Sgber
573235537Sgber	cg = &chip->chip_geom;
574235537Sgber	page = offset_to_page(cg, offset);
575235537Sgber	begin_off = offset - page * cg->page_size;
576235537Sgber	if (begin_off) {
577235537Sgber		begin = cg->page_size - begin_off;
578235537Sgber		len -= begin;
579235537Sgber	}
580235537Sgber	num = len / cg->page_size;
581235537Sgber	end = len % cg->page_size;
582235537Sgber
583235537Sgber	nandbus = device_get_parent(chip->dev);
584235537Sgber	NANDBUS_LOCK(nandbus);
585235537Sgber	NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
586235537Sgber
587235537Sgber	ptr = (uint8_t *)buf;
588235537Sgber	if (begin_off) {
589235537Sgber		if (NAND_READ_PAGE(chip->dev, page, ptr, begin, begin_off)) {
590235537Sgber			NANDBUS_UNLOCK(nandbus);
591235537Sgber			return (ENXIO);
592235537Sgber		}
593235537Sgber
594235537Sgber		page++;
595235537Sgber		ptr += begin;
596235537Sgber	}
597235537Sgber
598235537Sgber	while (num--) {
599235537Sgber		if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
600235537Sgber			NANDBUS_UNLOCK(nandbus);
601235537Sgber			return (ENXIO);
602235537Sgber		}
603235537Sgber
604235537Sgber		page++;
605235537Sgber		ptr += cg->page_size;
606235537Sgber	}
607235537Sgber
608235537Sgber	if (end)
609235537Sgber		if (NAND_READ_PAGE(chip->dev, page, ptr, end, 0)) {
610235537Sgber			NANDBUS_UNLOCK(nandbus);
611235537Sgber			return (ENXIO);
612235537Sgber		}
613235537Sgber
614235537Sgber	NANDBUS_UNLOCK(nandbus);
615235537Sgber
616235537Sgber	return (retval);
617235537Sgber}
618235537Sgber
619235537Sgber
620235537Sgberint
621235537Sgbernand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf,
622235537Sgber    uint32_t len)
623235537Sgber{
624235537Sgber	struct chip_geom *cg;
625235537Sgber	struct page_stat *pg_stat;
626235537Sgber	struct nand_ecc_data *eccd;
627235537Sgber	device_t nandbus;
628235537Sgber	uint32_t page, num;
629235537Sgber	uint8_t *oob = NULL;
630235537Sgber	uint16_t *eccpos = NULL;
631235537Sgber	int steps = 0, i, needwrite, err = 0;
632235537Sgber
633235537Sgber	nand_debug(NDBG_NAND,"%p prog page %x[%x]", chip, offset, len);
634235537Sgber
635235537Sgber	eccd = &chip->nand->ecc;
636235537Sgber	cg = &chip->chip_geom;
637235537Sgber	page = offset_to_page(cg, offset);
638235537Sgber	num = len / cg->page_size;
639235537Sgber
640235537Sgber	if (eccd->eccmode != NAND_ECC_NONE) {
641235537Sgber		steps = cg->page_size / eccd->eccsize;
642235537Sgber		oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
643235537Sgber		eccpos = default_software_ecc_positions(chip);
644235537Sgber	}
645235537Sgber
646235537Sgber	nandbus = device_get_parent(chip->dev);
647235537Sgber	NANDBUS_LOCK(nandbus);
648235537Sgber	NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
649235537Sgber
650235537Sgber	while (num--) {
651235537Sgber		if (NAND_PROGRAM_PAGE(chip->dev, page, buf, cg->page_size, 0)) {
652235537Sgber			err = ENXIO;
653235537Sgber			break;
654235537Sgber		}
655235537Sgber
656235537Sgber		if (eccd->eccmode != NAND_ECC_NONE) {
657235537Sgber			if (NAND_GET_ECC(chip->dev, buf, &eccd->ecccalculated,
658235537Sgber			    &needwrite)) {
659235537Sgber				err = ENXIO;
660235537Sgber				break;
661235537Sgber			}
662235537Sgber			nand_debug(NDBG_ECC,"ECC calculated:");
663235537Sgber			if (nand_debug_flag & NDBG_ECC)
664235537Sgber				for (i = 0; i < (eccd->eccbytes * steps); i++)
665235537Sgber					printf("%x ", eccd->ecccalculated[i]);
666235537Sgber
667235537Sgber			nand_debug(NDBG_ECC,"\n");
668235537Sgber
669235537Sgber			if (needwrite) {
670235537Sgber				if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
671235537Sgber				    0)) {
672235537Sgber					err = ENXIO;
673235537Sgber					break;
674235537Sgber				}
675235537Sgber
676235537Sgber				for (i = 0; i < (eccd->eccbytes * steps); i++)
677235537Sgber					oob[eccpos[i]] = eccd->ecccalculated[i];
678235537Sgber
679235537Sgber				if (NAND_PROGRAM_OOB(chip->dev, page, oob,
680235537Sgber				    cg->oob_size, 0)) {
681235537Sgber					err = ENXIO;
682235537Sgber					break;
683235537Sgber				}
684235537Sgber			}
685235537Sgber		}
686235537Sgber
687235537Sgber		pg_stat = &(chip->pg_stat[page]);
688235537Sgber		pg_stat->page_written++;
689235537Sgber
690235537Sgber		page++;
691235537Sgber		buf += cg->page_size;
692235537Sgber	}
693235537Sgber
694235537Sgber	NANDBUS_UNLOCK(nandbus);
695235537Sgber
696235537Sgber	if (oob)
697235537Sgber		free(oob, M_NAND);
698235537Sgber
699235537Sgber	return (err);
700235537Sgber}
701235537Sgber
702235537Sgberint
703235537Sgbernand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
704235537Sgber    uint32_t len)
705235537Sgber{
706235537Sgber	struct chip_geom *cg;
707235537Sgber	device_t nandbus;
708235537Sgber	uint8_t *ptr;
709235537Sgber	uint32_t page, num, end, begin = 0, begin_off;
710235537Sgber	int retval = 0;
711235537Sgber
712235537Sgber	cg = &chip->chip_geom;
713235537Sgber	page = offset_to_page(cg, offset);
714235537Sgber	begin_off = offset - page * cg->page_size;
715235537Sgber	if (begin_off) {
716235537Sgber		begin = cg->page_size - begin_off;
717235537Sgber		len -= begin;
718235537Sgber	}
719235537Sgber	num = len / cg->page_size;
720235537Sgber	end = len % cg->page_size;
721235537Sgber
722235537Sgber	nandbus = device_get_parent(chip->dev);
723235537Sgber	NANDBUS_LOCK(nandbus);
724235537Sgber	NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
725235537Sgber
726235537Sgber	ptr = (uint8_t *)buf;
727235537Sgber	if (begin_off) {
728235537Sgber		if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, begin, begin_off)) {
729235537Sgber			NANDBUS_UNLOCK(nandbus);
730235537Sgber			return (ENXIO);
731235537Sgber		}
732235537Sgber
733235537Sgber		page++;
734235537Sgber		ptr += begin;
735235537Sgber	}
736235537Sgber
737235537Sgber	while (num--) {
738235537Sgber		if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
739235537Sgber			NANDBUS_UNLOCK(nandbus);
740235537Sgber			return (ENXIO);
741235537Sgber		}
742235537Sgber
743235537Sgber		page++;
744235537Sgber		ptr += cg->page_size;
745235537Sgber	}
746235537Sgber
747235537Sgber	if (end)
748235537Sgber		retval = NAND_PROGRAM_PAGE(chip->dev, page, ptr, end, 0);
749235537Sgber
750235537Sgber	NANDBUS_UNLOCK(nandbus);
751235537Sgber
752235537Sgber	return (retval);
753235537Sgber}
754235537Sgber
755235537Sgberint
756235537Sgbernand_read_oob(struct nand_chip *chip, uint32_t page, void *buf,
757235537Sgber    uint32_t len)
758235537Sgber{
759235537Sgber	device_t nandbus;
760235537Sgber	int retval = 0;
761235537Sgber
762235537Sgber	nandbus = device_get_parent(chip->dev);
763235537Sgber	NANDBUS_LOCK(nandbus);
764235537Sgber	NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
765235537Sgber
766235537Sgber	retval = NAND_READ_OOB(chip->dev, page, buf, len, 0);
767235537Sgber
768235537Sgber	NANDBUS_UNLOCK(nandbus);
769235537Sgber
770235537Sgber	return (retval);
771235537Sgber}
772235537Sgber
773235537Sgber
774235537Sgberint
775235537Sgbernand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf,
776235537Sgber    uint32_t len)
777235537Sgber{
778235537Sgber	device_t nandbus;
779235537Sgber	int retval = 0;
780235537Sgber
781235537Sgber	nandbus = device_get_parent(chip->dev);
782235537Sgber	NANDBUS_LOCK(nandbus);
783235537Sgber	NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
784235537Sgber
785235537Sgber	retval = NAND_PROGRAM_OOB(chip->dev, page, buf, len, 0);
786235537Sgber
787235537Sgber	NANDBUS_UNLOCK(nandbus);
788235537Sgber
789235537Sgber	return (retval);
790235537Sgber}
791235537Sgber
792235537Sgberint
793235537Sgbernand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len)
794235537Sgber{
795235537Sgber	device_t nandbus;
796235537Sgber	struct chip_geom *cg;
797235537Sgber	uint32_t block, num_blocks;
798235537Sgber	int err = 0;
799235537Sgber
800235537Sgber	cg = &chip->chip_geom;
801235537Sgber	if ((offset % cg->block_size) || (len % cg->block_size))
802235537Sgber		return (EINVAL);
803235537Sgber
804235537Sgber	block = offset / cg->block_size;
805235537Sgber	num_blocks = len / cg->block_size;
806235537Sgber	nand_debug(NDBG_NAND,"%p erase blocks %d[%d]", chip, block, num_blocks);
807235537Sgber
808235537Sgber	nandbus = device_get_parent(chip->dev);
809235537Sgber	NANDBUS_LOCK(nandbus);
810235537Sgber	NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
811235537Sgber
812235537Sgber	while (num_blocks--) {
813235537Sgber		if (!nand_check_bad_block(chip, block)) {
814235537Sgber			if (NAND_ERASE_BLOCK(chip->dev, block)) {
815235537Sgber				nand_debug(NDBG_NAND,"%p erase blocks %d error",
816235537Sgber				    chip, block);
817235537Sgber				nand_mark_bad_block(chip, block);
818235537Sgber				err = ENXIO;
819235537Sgber			}
820235537Sgber		} else
821235537Sgber			err = ENXIO;
822235537Sgber
823235537Sgber		block++;
824235537Sgber	};
825235537Sgber
826235537Sgber	NANDBUS_UNLOCK(nandbus);
827235537Sgber
828235537Sgber	if (err)
829235537Sgber		nand_update_bbt(chip);
830235537Sgber
831235537Sgber	return (err);
832235537Sgber}
833237605Stakawata
834237605StakawataMODULE_VERSION(nand, 1);
835