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/* Generic NAND driver */
28235537Sgber
29235537Sgber#include <sys/cdefs.h>
30235537Sgber__FBSDID("$FreeBSD$");
31235537Sgber
32235537Sgber#include <sys/param.h>
33235537Sgber#include <sys/systm.h>
34235537Sgber#include <sys/proc.h>
35235537Sgber#include <sys/bus.h>
36235537Sgber#include <sys/conf.h>
37235537Sgber#include <sys/kernel.h>
38235537Sgber#include <sys/module.h>
39235537Sgber#include <sys/rman.h>
40235537Sgber#include <sys/lock.h>
41235537Sgber#include <sys/mutex.h>
42235537Sgber#include <sys/time.h>
43235537Sgber#include <sys/malloc.h>
44235537Sgber
45235537Sgber#include <dev/nand/nand.h>
46235537Sgber#include <dev/nand/nandbus.h>
47235537Sgber#include "nfc_if.h"
48235537Sgber#include "nand_if.h"
49235537Sgber#include "nandbus_if.h"
50235537Sgber
51235537Sgber
52235537Sgberstatic int onfi_nand_probe(device_t dev);
53235537Sgberstatic int large_nand_probe(device_t dev);
54235537Sgberstatic int small_nand_probe(device_t dev);
55235537Sgberstatic int generic_nand_attach(device_t dev);
56235537Sgberstatic int generic_nand_detach(device_t dev);
57235537Sgber
58235537Sgberstatic int generic_erase_block(device_t, uint32_t);
59235537Sgberstatic int generic_erase_block_intlv(device_t, uint32_t);
60235537Sgberstatic int generic_read_page (device_t, uint32_t, void *, uint32_t, uint32_t);
61235537Sgberstatic int generic_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
62235537Sgberstatic int generic_program_page(device_t, uint32_t, void *, uint32_t, uint32_t);
63235537Sgberstatic int generic_program_page_intlv(device_t, uint32_t, void *, uint32_t,
64235537Sgber    uint32_t);
65235537Sgberstatic int generic_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
66235537Sgberstatic int generic_is_blk_bad(device_t, uint32_t, uint8_t *);
67235537Sgberstatic int generic_get_ecc(device_t, void *, void *, int *);
68235537Sgberstatic int generic_correct_ecc(device_t, void *, void *, void *);
69235537Sgber
70235537Sgberstatic int small_read_page(device_t, uint32_t, void *, uint32_t, uint32_t);
71235537Sgberstatic int small_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
72235537Sgberstatic int small_program_page(device_t, uint32_t, void *, uint32_t, uint32_t);
73235537Sgberstatic int small_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
74235537Sgber
75235537Sgberstatic int onfi_is_blk_bad(device_t, uint32_t, uint8_t *);
76235537Sgberstatic int onfi_read_parameter(struct nand_chip *, struct onfi_params *);
77235537Sgber
78235537Sgberstatic int nand_send_address(device_t, int32_t, int32_t, int8_t);
79235537Sgber
80235537Sgberstatic device_method_t onand_methods[] = {
81235537Sgber	/* Device interface */
82235537Sgber	DEVMETHOD(device_probe,			onfi_nand_probe),
83235537Sgber	DEVMETHOD(device_attach,		generic_nand_attach),
84235537Sgber	DEVMETHOD(device_detach,		generic_nand_detach),
85235537Sgber
86235537Sgber	DEVMETHOD(nand_read_page,		generic_read_page),
87235537Sgber	DEVMETHOD(nand_program_page,		generic_program_page),
88235537Sgber	DEVMETHOD(nand_program_page_intlv,	generic_program_page_intlv),
89235537Sgber	DEVMETHOD(nand_read_oob,		generic_read_oob),
90235537Sgber	DEVMETHOD(nand_program_oob,		generic_program_oob),
91235537Sgber	DEVMETHOD(nand_erase_block,		generic_erase_block),
92235537Sgber	DEVMETHOD(nand_erase_block_intlv,	generic_erase_block_intlv),
93235537Sgber
94235537Sgber	DEVMETHOD(nand_is_blk_bad,		onfi_is_blk_bad),
95235537Sgber	DEVMETHOD(nand_get_ecc,			generic_get_ecc),
96235537Sgber	DEVMETHOD(nand_correct_ecc,		generic_correct_ecc),
97235537Sgber	{ 0, 0 }
98235537Sgber};
99235537Sgber
100235537Sgberstatic device_method_t lnand_methods[] = {
101235537Sgber	/* Device interface */
102235537Sgber	DEVMETHOD(device_probe,		large_nand_probe),
103235537Sgber	DEVMETHOD(device_attach,	generic_nand_attach),
104235537Sgber	DEVMETHOD(device_detach,	generic_nand_detach),
105235537Sgber
106235537Sgber	DEVMETHOD(nand_read_page,	generic_read_page),
107235537Sgber	DEVMETHOD(nand_program_page,	generic_program_page),
108235537Sgber	DEVMETHOD(nand_read_oob,	generic_read_oob),
109235537Sgber	DEVMETHOD(nand_program_oob,	generic_program_oob),
110235537Sgber	DEVMETHOD(nand_erase_block,	generic_erase_block),
111235537Sgber
112235537Sgber	DEVMETHOD(nand_is_blk_bad,	generic_is_blk_bad),
113235537Sgber	DEVMETHOD(nand_get_ecc,		generic_get_ecc),
114235537Sgber	DEVMETHOD(nand_correct_ecc,	generic_correct_ecc),
115235537Sgber	{ 0, 0 }
116235537Sgber};
117235537Sgber
118235537Sgberstatic device_method_t snand_methods[] = {
119235537Sgber	/* Device interface */
120235537Sgber	DEVMETHOD(device_probe,		small_nand_probe),
121235537Sgber	DEVMETHOD(device_attach,	generic_nand_attach),
122235537Sgber	DEVMETHOD(device_detach,	generic_nand_detach),
123235537Sgber
124235537Sgber	DEVMETHOD(nand_read_page,	small_read_page),
125235537Sgber	DEVMETHOD(nand_program_page,	small_program_page),
126235537Sgber	DEVMETHOD(nand_read_oob,	small_read_oob),
127235537Sgber	DEVMETHOD(nand_program_oob,	small_program_oob),
128235537Sgber	DEVMETHOD(nand_erase_block,	generic_erase_block),
129235537Sgber
130235537Sgber	DEVMETHOD(nand_is_blk_bad,	generic_is_blk_bad),
131235537Sgber	DEVMETHOD(nand_get_ecc,		generic_get_ecc),
132235537Sgber	DEVMETHOD(nand_correct_ecc,	generic_correct_ecc),
133235537Sgber	{ 0, 0 }
134235537Sgber};
135235537Sgber
136235537Sgberdevclass_t onand_devclass;
137235537Sgberdevclass_t lnand_devclass;
138235537Sgberdevclass_t snand_devclass;
139235537Sgber
140235537Sgberdriver_t onand_driver = {
141235537Sgber	"onand",
142235537Sgber	onand_methods,
143235537Sgber	sizeof(struct nand_chip)
144235537Sgber};
145235537Sgber
146235537Sgberdriver_t lnand_driver = {
147235537Sgber	"lnand",
148235537Sgber	lnand_methods,
149235537Sgber	sizeof(struct nand_chip)
150235537Sgber};
151235537Sgber
152235537Sgberdriver_t snand_driver = {
153235537Sgber	"snand",
154235537Sgber	snand_methods,
155235537Sgber	sizeof(struct nand_chip)
156235537Sgber};
157235537Sgber
158235537SgberDRIVER_MODULE(onand, nandbus, onand_driver, onand_devclass, 0, 0);
159235537SgberDRIVER_MODULE(lnand, nandbus, lnand_driver, lnand_devclass, 0, 0);
160235537SgberDRIVER_MODULE(snand, nandbus, snand_driver, snand_devclass, 0, 0);
161235537Sgber
162235537Sgberstatic int
163235537Sgberonfi_nand_probe(device_t dev)
164235537Sgber{
165235537Sgber	struct nandbus_ivar *ivar;
166235537Sgber
167235537Sgber	ivar = device_get_ivars(dev);
168235537Sgber	if (ivar && ivar->is_onfi) {
169235537Sgber		device_set_desc(dev, "ONFI compliant NAND");
170235537Sgber		return (BUS_PROBE_DEFAULT);
171235537Sgber	}
172235537Sgber
173235537Sgber	return (ENODEV);
174235537Sgber}
175235537Sgber
176235537Sgberstatic int
177235537Sgberlarge_nand_probe(device_t dev)
178235537Sgber{
179235537Sgber	struct nandbus_ivar *ivar;
180235537Sgber
181235537Sgber	ivar = device_get_ivars(dev);
182235537Sgber	if (ivar && !ivar->is_onfi && ivar->params->page_size >= 512) {
183235537Sgber		device_set_desc(dev, ivar->params->name);
184235537Sgber		return (BUS_PROBE_DEFAULT);
185235537Sgber	}
186235537Sgber
187235537Sgber	return (ENODEV);
188235537Sgber}
189235537Sgber
190235537Sgberstatic int
191235537Sgbersmall_nand_probe(device_t dev)
192235537Sgber{
193235537Sgber	struct nandbus_ivar *ivar;
194235537Sgber
195235537Sgber	ivar = device_get_ivars(dev);
196235537Sgber	if (ivar && !ivar->is_onfi && ivar->params->page_size == 512) {
197235537Sgber		device_set_desc(dev, ivar->params->name);
198235537Sgber		return (BUS_PROBE_DEFAULT);
199235537Sgber	}
200235537Sgber
201235537Sgber	return (ENODEV);
202235537Sgber}
203235537Sgber
204235537Sgberstatic int
205235537Sgbergeneric_nand_attach(device_t dev)
206235537Sgber{
207235537Sgber	struct nand_chip *chip;
208235537Sgber	struct nandbus_ivar *ivar;
209235537Sgber	struct onfi_params *onfi_params;
210235537Sgber	device_t nandbus, nfc;
211235537Sgber	int err;
212235537Sgber
213235537Sgber	chip = device_get_softc(dev);
214235537Sgber	chip->dev = dev;
215235537Sgber
216235537Sgber	ivar = device_get_ivars(dev);
217235537Sgber	chip->id.man_id = ivar->man_id;
218235537Sgber	chip->id.dev_id = ivar->dev_id;
219235537Sgber	chip->num = ivar->cs;
220235537Sgber
221235537Sgber	/* TODO remove when HW ECC supported */
222235537Sgber	nandbus = device_get_parent(dev);
223235537Sgber	nfc = device_get_parent(nandbus);
224235537Sgber
225235537Sgber	chip->nand = device_get_softc(nfc);
226235537Sgber
227235537Sgber	if (ivar->is_onfi) {
228235537Sgber		onfi_params = malloc(sizeof(struct onfi_params),
229235537Sgber		    M_NAND, M_WAITOK | M_ZERO);
230235537Sgber		if (onfi_params == NULL)
231235537Sgber			return (ENXIO);
232235537Sgber
233235537Sgber		if (onfi_read_parameter(chip, onfi_params)) {
234235537Sgber			nand_debug(NDBG_GEN,"Could not read parameter page!\n");
235235537Sgber			free(onfi_params, M_NAND);
236235537Sgber			return (ENXIO);
237235537Sgber		}
238235537Sgber
239235537Sgber		nand_onfi_set_params(chip, onfi_params);
240235537Sgber		/* Set proper column and row cycles */
241235537Sgber		ivar->cols = (onfi_params->address_cycles >> 4) & 0xf;
242235537Sgber		ivar->rows = onfi_params->address_cycles & 0xf;
243235537Sgber		free(onfi_params, M_NAND);
244235537Sgber
245235537Sgber	} else {
246235537Sgber
247235537Sgber		nand_set_params(chip, ivar->params);
248235537Sgber	}
249235537Sgber
250235537Sgber	err = nand_init_stat(chip);
251235537Sgber	if (err) {
252235537Sgber		generic_nand_detach(dev);
253235537Sgber		return (err);
254235537Sgber	}
255235537Sgber
256235537Sgber	err = nand_init_bbt(chip);
257235537Sgber	if (err) {
258235537Sgber		generic_nand_detach(dev);
259235537Sgber		return (err);
260235537Sgber	}
261235537Sgber
262235537Sgber	err = nand_make_dev(chip);
263235537Sgber	if (err) {
264235537Sgber		generic_nand_detach(dev);
265235537Sgber		return (err);
266235537Sgber	}
267235537Sgber
268235537Sgber	err = create_geom_disk(chip);
269235537Sgber	if (err) {
270235537Sgber		generic_nand_detach(dev);
271235537Sgber		return (err);
272235537Sgber	}
273235537Sgber
274235537Sgber	return (0);
275235537Sgber}
276235537Sgber
277235537Sgberstatic int
278235537Sgbergeneric_nand_detach(device_t dev)
279235537Sgber{
280235537Sgber	struct nand_chip *chip;
281235537Sgber
282235537Sgber	chip = device_get_softc(dev);
283235537Sgber
284235537Sgber	nand_destroy_bbt(chip);
285235537Sgber	destroy_geom_disk(chip);
286235537Sgber	nand_destroy_dev(chip);
287235537Sgber	nand_destroy_stat(chip);
288235537Sgber
289235537Sgber	return (0);
290235537Sgber}
291235537Sgber
292235537Sgberstatic int
293235537Sgbercan_write(device_t nandbus)
294235537Sgber{
295235537Sgber	uint8_t status;
296235537Sgber
297235537Sgber	if (NANDBUS_WAIT_READY(nandbus, &status))
298235537Sgber		return (0);
299235537Sgber
300235537Sgber	if (!(status & NAND_STATUS_WP)) {
301235537Sgber		nand_debug(NDBG_GEN,"Chip is write-protected");
302235537Sgber		return (0);
303235537Sgber	}
304235537Sgber
305235537Sgber	return (1);
306235537Sgber}
307235537Sgber
308235537Sgberstatic int
309235537Sgbercheck_fail(device_t nandbus)
310235537Sgber{
311235537Sgber	uint8_t status;
312235537Sgber
313235537Sgber	NANDBUS_WAIT_READY(nandbus, &status);
314235537Sgber	if (status & NAND_STATUS_FAIL) {
315235537Sgber		nand_debug(NDBG_GEN,"Status failed %x", status);
316235537Sgber		return (ENXIO);
317235537Sgber	}
318235537Sgber
319235537Sgber	return (0);
320235537Sgber}
321235537Sgber
322235537Sgberstatic int
323235537Sgberonfi_read_parameter(struct nand_chip *chip, struct onfi_params *params)
324235537Sgber{
325235537Sgber	device_t nandbus;
326235537Sgber
327235537Sgber	nand_debug(NDBG_GEN,"read parameter");
328235537Sgber
329235537Sgber	nandbus = device_get_parent(chip->dev);
330235537Sgber
331235537Sgber	NANDBUS_SELECT_CS(nandbus, chip->num);
332235537Sgber
333235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ_PARAMETER))
334235537Sgber		return (ENXIO);
335235537Sgber
336235537Sgber	if (nand_send_address(chip->dev, -1, -1, PAGE_PARAMETER_DEF))
337235537Sgber		return (ENXIO);
338235537Sgber
339235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
340235537Sgber		return (ENXIO);
341235537Sgber
342235537Sgber	NANDBUS_READ_BUFFER(nandbus, params, sizeof(struct onfi_params));
343235537Sgber
344235537Sgber	/* TODO */
345235537Sgber	/* Check for signature */
346235537Sgber	/* Check CRC */
347235537Sgber	/* Use redundant page if necessary */
348235537Sgber
349235537Sgber	return (0);
350235537Sgber}
351235537Sgber
352235537Sgberstatic int
353235537Sgbersend_read_page(device_t nand, uint8_t start_command, uint8_t end_command,
354235537Sgber    uint32_t row, uint32_t column)
355235537Sgber{
356235537Sgber	device_t nandbus = device_get_parent(nand);
357235537Sgber
358235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, start_command))
359235537Sgber		return (ENXIO);
360235537Sgber
361235537Sgber	if (nand_send_address(nand, row, column, -1))
362235537Sgber		return (ENXIO);
363235537Sgber
364235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, end_command))
365235537Sgber		return (ENXIO);
366235537Sgber
367235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
368235537Sgber		return (ENXIO);
369235537Sgber
370235537Sgber	return (0);
371235537Sgber}
372235537Sgber
373235537Sgberstatic int
374235537Sgbergeneric_read_page(device_t nand, uint32_t page, void *buf, uint32_t len,
375235537Sgber    uint32_t offset)
376235537Sgber{
377235537Sgber	struct nand_chip *chip;
378235537Sgber	struct page_stat *pg_stat;
379235537Sgber	device_t nandbus;
380235537Sgber	uint32_t row;
381235537Sgber
382235537Sgber	nand_debug(NDBG_GEN,"%p raw read page %x[%x] at %x", nand, page, len, offset);
383235537Sgber	chip = device_get_softc(nand);
384235537Sgber	nandbus = device_get_parent(nand);
385235537Sgber
386235537Sgber	if (nand_check_page_boundary(chip, page))
387235537Sgber		return (ENXIO);
388235537Sgber
389235537Sgber	page_to_row(&chip->chip_geom, page, &row);
390235537Sgber
391235537Sgber	if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row,
392235537Sgber	    offset))
393235537Sgber		return (ENXIO);
394235537Sgber
395235537Sgber	DELAY(chip->t_r);
396235537Sgber
397235537Sgber	NANDBUS_READ_BUFFER(nandbus, buf, len);
398235537Sgber
399235537Sgber	if (check_fail(nandbus))
400235537Sgber		return (ENXIO);
401235537Sgber
402235537Sgber	pg_stat = &(chip->pg_stat[page]);
403235537Sgber	pg_stat->page_raw_read++;
404235537Sgber
405235537Sgber	return (0);
406235537Sgber}
407235537Sgber
408235537Sgberstatic int
409235537Sgbergeneric_read_oob(device_t nand, uint32_t page, void* buf, uint32_t len,
410235537Sgber    uint32_t offset)
411235537Sgber{
412235537Sgber	struct nand_chip *chip;
413235537Sgber	device_t nandbus;
414235537Sgber	uint32_t row;
415235537Sgber
416235537Sgber	nand_debug(NDBG_GEN,"%p raw read oob %x[%x] at %x", nand, page, len, offset);
417235537Sgber	chip = device_get_softc(nand);
418235537Sgber	nandbus = device_get_parent(nand);
419235537Sgber
420235537Sgber	if (nand_check_page_boundary(chip, page)) {
421235537Sgber		nand_debug(NDBG_GEN,"page boundary check failed: %08x\n", page);
422235537Sgber		return (ENXIO);
423235537Sgber	}
424235537Sgber
425235537Sgber	page_to_row(&chip->chip_geom, page, &row);
426235537Sgber
427235537Sgber	offset += chip->chip_geom.page_size;
428235537Sgber
429235537Sgber	if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row,
430235537Sgber	    offset))
431235537Sgber		return (ENXIO);
432235537Sgber
433235537Sgber	DELAY(chip->t_r);
434235537Sgber
435235537Sgber	NANDBUS_READ_BUFFER(nandbus, buf, len);
436235537Sgber
437235537Sgber	if (check_fail(nandbus))
438235537Sgber		return (ENXIO);
439235537Sgber
440235537Sgber	return (0);
441235537Sgber}
442235537Sgber
443235537Sgberstatic int
444235537Sgbersend_start_program_page(device_t nand, uint32_t row, uint32_t column)
445235537Sgber{
446235537Sgber	device_t nandbus = device_get_parent(nand);
447235537Sgber
448235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_PROG))
449235537Sgber		return (ENXIO);
450235537Sgber
451235537Sgber	if (nand_send_address(nand, row, column, -1))
452235537Sgber		return (ENXIO);
453235537Sgber
454235537Sgber	return (0);
455235537Sgber}
456235537Sgber
457235537Sgberstatic int
458235537Sgbersend_end_program_page(device_t nandbus, uint8_t end_command)
459235537Sgber{
460235537Sgber
461235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, end_command))
462235537Sgber		return (ENXIO);
463235537Sgber
464235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
465235537Sgber		return (ENXIO);
466235537Sgber
467235537Sgber	return (0);
468235537Sgber}
469235537Sgber
470235537Sgberstatic int
471235537Sgbergeneric_program_page(device_t nand, uint32_t page, void *buf, uint32_t len,
472235537Sgber    uint32_t offset)
473235537Sgber{
474235537Sgber	struct nand_chip *chip;
475235537Sgber	struct page_stat *pg_stat;
476235537Sgber	device_t nandbus;
477235537Sgber	uint32_t row;
478235537Sgber
479235537Sgber	nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len,
480235537Sgber	    offset);
481235537Sgber	chip = device_get_softc(nand);
482235537Sgber	nandbus = device_get_parent(nand);
483235537Sgber
484235537Sgber	if (nand_check_page_boundary(chip, page))
485235537Sgber		return (ENXIO);
486235537Sgber
487235537Sgber	page_to_row(&chip->chip_geom, page, &row);
488235537Sgber
489235537Sgber	if (!can_write(nandbus))
490235537Sgber		return (ENXIO);
491235537Sgber
492235537Sgber	if (send_start_program_page(nand, row, offset))
493235537Sgber		return (ENXIO);
494235537Sgber
495235537Sgber	NANDBUS_WRITE_BUFFER(nandbus, buf, len);
496235537Sgber
497235537Sgber	if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
498235537Sgber		return (ENXIO);
499235537Sgber
500235537Sgber	DELAY(chip->t_prog);
501235537Sgber
502235537Sgber	if (check_fail(nandbus))
503235537Sgber		return (ENXIO);
504235537Sgber
505235537Sgber	pg_stat = &(chip->pg_stat[page]);
506235537Sgber	pg_stat->page_raw_written++;
507235537Sgber
508235537Sgber	return (0);
509235537Sgber}
510235537Sgber
511235537Sgberstatic int
512235537Sgbergeneric_program_page_intlv(device_t nand, uint32_t page, void *buf,
513235537Sgber    uint32_t len, uint32_t offset)
514235537Sgber{
515235537Sgber	struct nand_chip *chip;
516235537Sgber	struct page_stat *pg_stat;
517235537Sgber	device_t nandbus;
518235537Sgber	uint32_t row;
519235537Sgber
520235537Sgber	nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len, offset);
521235537Sgber	chip = device_get_softc(nand);
522235537Sgber	nandbus = device_get_parent(nand);
523235537Sgber
524235537Sgber	if (nand_check_page_boundary(chip, page))
525235537Sgber		return (ENXIO);
526235537Sgber
527235537Sgber	page_to_row(&chip->chip_geom, page, &row);
528235537Sgber
529235537Sgber	if (!can_write(nandbus))
530235537Sgber		return (ENXIO);
531235537Sgber
532235537Sgber	if (send_start_program_page(nand, row, offset))
533235537Sgber		return (ENXIO);
534235537Sgber
535235537Sgber	NANDBUS_WRITE_BUFFER(nandbus, buf, len);
536235537Sgber
537235537Sgber	if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV))
538235537Sgber		return (ENXIO);
539235537Sgber
540235537Sgber	DELAY(chip->t_prog);
541235537Sgber
542235537Sgber	if (check_fail(nandbus))
543235537Sgber		return (ENXIO);
544235537Sgber
545235537Sgber	pg_stat = &(chip->pg_stat[page]);
546235537Sgber	pg_stat->page_raw_written++;
547235537Sgber
548235537Sgber	return (0);
549235537Sgber}
550235537Sgber
551235537Sgberstatic int
552235537Sgbergeneric_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len,
553235537Sgber    uint32_t offset)
554235537Sgber{
555235537Sgber	struct nand_chip *chip;
556235537Sgber	device_t nandbus;
557235537Sgber	uint32_t row;
558235537Sgber
559235537Sgber	nand_debug(NDBG_GEN,"%p raw prog oob %x[%x] at %x", nand, page, len,
560235537Sgber	    offset);
561235537Sgber	chip = device_get_softc(nand);
562235537Sgber	nandbus = device_get_parent(nand);
563235537Sgber
564235537Sgber	if (nand_check_page_boundary(chip, page))
565235537Sgber		return (ENXIO);
566235537Sgber
567235537Sgber	page_to_row(&chip->chip_geom, page, &row);
568235537Sgber	offset += chip->chip_geom.page_size;
569235537Sgber
570235537Sgber	if (!can_write(nandbus))
571235537Sgber		return (ENXIO);
572235537Sgber
573235537Sgber	if (send_start_program_page(nand, row, offset))
574235537Sgber		return (ENXIO);
575235537Sgber
576235537Sgber	NANDBUS_WRITE_BUFFER(nandbus, buf, len);
577235537Sgber
578235537Sgber	if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
579235537Sgber		return (ENXIO);
580235537Sgber
581235537Sgber	DELAY(chip->t_prog);
582235537Sgber
583235537Sgber	if (check_fail(nandbus))
584235537Sgber		return (ENXIO);
585235537Sgber
586235537Sgber	return (0);
587235537Sgber}
588235537Sgber
589235537Sgberstatic int
590235537Sgbersend_erase_block(device_t nand, uint32_t row, uint8_t second_command)
591235537Sgber{
592235537Sgber	device_t nandbus = device_get_parent(nand);
593235537Sgber
594235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_ERASE))
595235537Sgber		return (ENXIO);
596235537Sgber
597235537Sgber	if (nand_send_address(nand, row, -1, -1))
598235537Sgber		return (ENXIO);
599235537Sgber
600235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, second_command))
601235537Sgber		return (ENXIO);
602235537Sgber
603235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
604235537Sgber		return (ENXIO);
605235537Sgber
606235537Sgber	return (0);
607235537Sgber}
608235537Sgber
609235537Sgberstatic int
610235537Sgbergeneric_erase_block(device_t nand, uint32_t block)
611235537Sgber{
612235537Sgber	struct block_stat *blk_stat;
613235537Sgber	struct nand_chip *chip;
614235537Sgber	device_t nandbus;
615235537Sgber	int row;
616235537Sgber
617235537Sgber	nand_debug(NDBG_GEN,"%p erase block  %x", nand, block);
618235537Sgber	nandbus = device_get_parent(nand);
619235537Sgber	chip = device_get_softc(nand);
620235537Sgber
621235537Sgber	if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns))
622235537Sgber		return (ENXIO);
623235537Sgber
624235537Sgber	row = (block << chip->chip_geom.blk_shift) &
625235537Sgber	    chip->chip_geom.blk_mask;
626235537Sgber
627235537Sgber	nand_debug(NDBG_GEN,"%p erase block  row %x", nand, row);
628235537Sgber
629235537Sgber	if (!can_write(nandbus))
630235537Sgber		return (ENXIO);
631235537Sgber
632235537Sgber	send_erase_block(nand, row, NAND_CMD_ERASE_END);
633235537Sgber
634235537Sgber	DELAY(chip->t_bers);
635235537Sgber
636235537Sgber	if (check_fail(nandbus))
637235537Sgber		return (ENXIO);
638235537Sgber
639235537Sgber	blk_stat = &(chip->blk_stat[block]);
640235537Sgber	blk_stat->block_erased++;
641235537Sgber
642235537Sgber	return (0);
643235537Sgber}
644235537Sgber
645235537Sgberstatic int
646235537Sgbergeneric_erase_block_intlv(device_t nand, uint32_t block)
647235537Sgber{
648235537Sgber	struct block_stat *blk_stat;
649235537Sgber	struct nand_chip *chip;
650235537Sgber	device_t nandbus;
651235537Sgber	int row;
652235537Sgber
653235537Sgber	nand_debug(NDBG_GEN,"%p erase block  %x", nand, block);
654235537Sgber	nandbus = device_get_parent(nand);
655235537Sgber	chip = device_get_softc(nand);
656235537Sgber
657235537Sgber	if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns))
658235537Sgber		return (ENXIO);
659235537Sgber
660235537Sgber	row = (block << chip->chip_geom.blk_shift) &
661235537Sgber	    chip->chip_geom.blk_mask;
662235537Sgber
663235537Sgber	if (!can_write(nandbus))
664235537Sgber		return (ENXIO);
665235537Sgber
666235537Sgber	send_erase_block(nand, row, NAND_CMD_ERASE_INTLV);
667235537Sgber
668235537Sgber	DELAY(chip->t_bers);
669235537Sgber
670235537Sgber	if (check_fail(nandbus))
671235537Sgber		return (ENXIO);
672235537Sgber
673235537Sgber	blk_stat = &(chip->blk_stat[block]);
674235537Sgber	blk_stat->block_erased++;
675235537Sgber
676235537Sgber	return (0);
677235537Sgber
678235537Sgber}
679235537Sgber
680235537Sgberstatic int
681235537Sgberonfi_is_blk_bad(device_t device, uint32_t block_number, uint8_t *bad)
682235537Sgber{
683235537Sgber	struct nand_chip *chip;
684235537Sgber	int page_number, i, j, err;
685235537Sgber	uint8_t *oob;
686235537Sgber
687235537Sgber	chip = device_get_softc(device);
688235537Sgber
689235537Sgber	oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK);
690235537Sgber	if (!oob) {
691235537Sgber		device_printf(device, "%s: cannot allocate oob\n", __func__);
692235537Sgber		return (ENOMEM);
693235537Sgber	}
694235537Sgber
695235537Sgber	page_number = block_number * chip->chip_geom.pgs_per_blk;
696235537Sgber	*bad = 0;
697235537Sgber	/* Check OOB of first and last page */
698235537Sgber	for (i = 0; i < 2; i++, page_number+= chip->chip_geom.pgs_per_blk - 1) {
699235537Sgber		err = generic_read_oob(device, page_number, oob,
700235537Sgber		    chip->chip_geom.oob_size, 0);
701235537Sgber		if (err) {
702235537Sgber			device_printf(device, "%s: cannot allocate oob\n",
703235537Sgber			    __func__);
704235537Sgber			free(oob, M_NAND);
705235537Sgber			return (ENOMEM);
706235537Sgber		}
707235537Sgber
708235537Sgber		for (j = 0; j < chip->chip_geom.oob_size; j++) {
709235537Sgber			if (!oob[j]) {
710235537Sgber				*bad = 1;
711235537Sgber				free(oob, M_NAND);
712235537Sgber				return (0);
713235537Sgber			}
714235537Sgber		}
715235537Sgber	}
716235537Sgber
717235537Sgber	free(oob, M_NAND);
718235537Sgber
719235537Sgber	return (0);
720235537Sgber}
721235537Sgber
722235537Sgberstatic int
723235537Sgbersend_small_read_page(device_t nand, uint8_t start_command,
724235537Sgber    uint32_t row, uint32_t column)
725235537Sgber{
726235537Sgber	device_t nandbus = device_get_parent(nand);
727235537Sgber
728235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, start_command))
729235537Sgber		return (ENXIO);
730235537Sgber
731235537Sgber	if (nand_send_address(nand, row, column, -1))
732235537Sgber		return (ENXIO);
733235537Sgber
734235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
735235537Sgber		return (ENXIO);
736235537Sgber
737235537Sgber	return (0);
738235537Sgber}
739235537Sgber
740235537Sgber
741235537Sgberstatic int
742235537Sgbersmall_read_page(device_t nand, uint32_t page, void *buf, uint32_t len,
743235537Sgber    uint32_t offset)
744235537Sgber{
745235537Sgber	struct nand_chip *chip;
746235537Sgber	struct page_stat *pg_stat;
747235537Sgber	device_t nandbus;
748235537Sgber	uint32_t row;
749235537Sgber
750235537Sgber	nand_debug(NDBG_GEN,"%p small read page %x[%x] at %x", nand, page, len, offset);
751235537Sgber	chip = device_get_softc(nand);
752235537Sgber	nandbus = device_get_parent(nand);
753235537Sgber
754235537Sgber	if (nand_check_page_boundary(chip, page))
755235537Sgber		return (ENXIO);
756235537Sgber
757235537Sgber	page_to_row(&chip->chip_geom, page, &row);
758235537Sgber
759235537Sgber	if (offset < 256) {
760235537Sgber		if (send_small_read_page(nand, NAND_CMD_SMALLA, row, offset))
761235537Sgber			return (ENXIO);
762235537Sgber	} else {
763235537Sgber		offset -= 256;
764235537Sgber		if (send_small_read_page(nandbus, NAND_CMD_SMALLB, row, offset))
765235537Sgber			return (ENXIO);
766235537Sgber	}
767235537Sgber
768235537Sgber	DELAY(chip->t_r);
769235537Sgber
770235537Sgber	NANDBUS_READ_BUFFER(nandbus, buf, len);
771235537Sgber
772235537Sgber	if (check_fail(nandbus))
773235537Sgber		return (ENXIO);
774235537Sgber
775235537Sgber	pg_stat = &(chip->pg_stat[page]);
776235537Sgber	pg_stat->page_raw_read++;
777235537Sgber
778235537Sgber	return (0);
779235537Sgber}
780235537Sgber
781235537Sgberstatic int
782235537Sgbersmall_read_oob(device_t nand, uint32_t page, void *buf, uint32_t len,
783235537Sgber    uint32_t offset)
784235537Sgber{
785235537Sgber	struct nand_chip *chip;
786235537Sgber	struct page_stat *pg_stat;
787235537Sgber	device_t nandbus;
788235537Sgber	uint32_t row;
789235537Sgber
790235537Sgber	nand_debug(NDBG_GEN,"%p small read oob %x[%x] at %x", nand, page, len, offset);
791235537Sgber	chip = device_get_softc(nand);
792235537Sgber	nandbus = device_get_parent(nand);
793235537Sgber
794235537Sgber	if (nand_check_page_boundary(chip, page))
795235537Sgber		return (ENXIO);
796235537Sgber
797235537Sgber	page_to_row(&chip->chip_geom, page, &row);
798235537Sgber
799235537Sgber	if (send_small_read_page(nand, NAND_CMD_SMALLOOB, row, 0))
800235537Sgber		return (ENXIO);
801235537Sgber
802235537Sgber	DELAY(chip->t_r);
803235537Sgber
804235537Sgber	NANDBUS_READ_BUFFER(nandbus, buf, len);
805235537Sgber
806235537Sgber	if (check_fail(nandbus))
807235537Sgber		return (ENXIO);
808235537Sgber
809235537Sgber	pg_stat = &(chip->pg_stat[page]);
810235537Sgber	pg_stat->page_raw_read++;
811235537Sgber
812235537Sgber	return (0);
813235537Sgber}
814235537Sgber
815235537Sgberstatic int
816235537Sgbersmall_program_page(device_t nand, uint32_t page, void* buf, uint32_t len,
817235537Sgber    uint32_t offset)
818235537Sgber{
819235537Sgber	struct nand_chip *chip;
820235537Sgber	device_t nandbus;
821235537Sgber	uint32_t row;
822235537Sgber
823235537Sgber	nand_debug(NDBG_GEN,"%p small prog page %x[%x] at %x", nand, page, len, offset);
824235537Sgber	chip = device_get_softc(nand);
825235537Sgber	nandbus = device_get_parent(nand);
826235537Sgber
827235537Sgber	if (nand_check_page_boundary(chip, page))
828235537Sgber		return (ENXIO);
829235537Sgber
830235537Sgber	page_to_row(&chip->chip_geom, page, &row);
831235537Sgber
832235537Sgber	if (!can_write(nandbus))
833235537Sgber		return (ENXIO);
834235537Sgber
835235537Sgber	if (offset < 256) {
836235537Sgber		if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLA))
837235537Sgber			return (ENXIO);
838235537Sgber	} else {
839235537Sgber		if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLB))
840235537Sgber			return (ENXIO);
841235537Sgber	}
842235537Sgber
843235537Sgber	if (send_start_program_page(nand, row, offset))
844235537Sgber		return (ENXIO);
845235537Sgber
846235537Sgber	NANDBUS_WRITE_BUFFER(nandbus, buf, len);
847235537Sgber
848235537Sgber	if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
849235537Sgber		return (ENXIO);
850235537Sgber
851235537Sgber	DELAY(chip->t_prog);
852235537Sgber
853235537Sgber	if (check_fail(nandbus))
854235537Sgber		return (ENXIO);
855235537Sgber
856235537Sgber	return (0);
857235537Sgber}
858235537Sgber
859235537Sgberstatic int
860235537Sgbersmall_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len,
861235537Sgber    uint32_t offset)
862235537Sgber{
863235537Sgber	struct nand_chip *chip;
864235537Sgber	device_t nandbus;
865235537Sgber	uint32_t row;
866235537Sgber
867235537Sgber	nand_debug(NDBG_GEN,"%p small prog oob %x[%x] at %x", nand, page, len, offset);
868235537Sgber	chip = device_get_softc(nand);
869235537Sgber	nandbus = device_get_parent(nand);
870235537Sgber
871235537Sgber	if (nand_check_page_boundary(chip, page))
872235537Sgber		return (ENXIO);
873235537Sgber
874235537Sgber	page_to_row(&chip->chip_geom, page, &row);
875235537Sgber
876235537Sgber	if (!can_write(nandbus))
877235537Sgber		return (ENXIO);
878235537Sgber
879235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLOOB))
880235537Sgber		return (ENXIO);
881235537Sgber
882235537Sgber	if (send_start_program_page(nand, row, offset))
883235537Sgber		return (ENXIO);
884235537Sgber
885235537Sgber	NANDBUS_WRITE_BUFFER(nandbus, buf, len);
886235537Sgber
887235537Sgber	if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
888235537Sgber		return (ENXIO);
889235537Sgber
890235537Sgber	DELAY(chip->t_prog);
891235537Sgber
892235537Sgber	if (check_fail(nandbus))
893235537Sgber		return (ENXIO);
894235537Sgber
895235537Sgber	return (0);
896235537Sgber}
897235537Sgber
898235537Sgberint
899235537Sgbernand_send_address(device_t nand, int32_t row, int32_t col, int8_t id)
900235537Sgber{
901235537Sgber	struct nandbus_ivar *ivar;
902235537Sgber	device_t nandbus;
903235537Sgber	uint8_t addr;
904235537Sgber	int err = 0;
905235537Sgber	int i;
906235537Sgber
907235537Sgber	nandbus = device_get_parent(nand);
908235537Sgber	ivar = device_get_ivars(nand);
909235537Sgber
910235537Sgber	if (id != -1) {
911235537Sgber		nand_debug(NDBG_GEN,"send_address: send id %02x", id);
912235537Sgber		err = NANDBUS_SEND_ADDRESS(nandbus, id);
913235537Sgber	}
914235537Sgber
915235537Sgber	if (!err && col != -1) {
916235537Sgber		for (i = 0; i < ivar->cols; i++, col >>= 8) {
917235537Sgber			addr = (uint8_t)(col & 0xff);
918235537Sgber			nand_debug(NDBG_GEN,"send_address: send address column "
919235537Sgber			    "%02x", addr);
920235537Sgber			err = NANDBUS_SEND_ADDRESS(nandbus, addr);
921235537Sgber			if (err)
922235537Sgber				break;
923235537Sgber		}
924235537Sgber	}
925235537Sgber
926235537Sgber	if (!err && row != -1) {
927235537Sgber		for (i = 0; i < ivar->rows; i++, row >>= 8) {
928235537Sgber			addr = (uint8_t)(row & 0xff);
929235537Sgber			nand_debug(NDBG_GEN,"send_address: send address row "
930235537Sgber			    "%02x", addr);
931235537Sgber			err = NANDBUS_SEND_ADDRESS(nandbus, addr);
932235537Sgber			if (err)
933235537Sgber				break;
934235537Sgber		}
935235537Sgber	}
936235537Sgber
937235537Sgber	return (err);
938235537Sgber}
939235537Sgber
940235537Sgberstatic int
941235537Sgbergeneric_is_blk_bad(device_t dev, uint32_t block, uint8_t *bad)
942235537Sgber{
943235537Sgber	struct nand_chip *chip;
944235537Sgber	int page_number, err, i;
945235537Sgber	uint8_t *oob;
946235537Sgber
947235537Sgber	chip = device_get_softc(dev);
948235537Sgber
949235537Sgber	oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK);
950235537Sgber	if (!oob) {
951235537Sgber		device_printf(dev, "%s: cannot allocate OOB\n", __func__);
952235537Sgber		return (ENOMEM);
953235537Sgber	}
954235537Sgber
955235537Sgber	page_number = block * chip->chip_geom.pgs_per_blk;
956235537Sgber	*bad = 0;
957235537Sgber
958235537Sgber	/* Check OOB of first and second page */
959235537Sgber	for (i = 0; i < 2; i++) {
960235537Sgber		err = NAND_READ_OOB(dev, page_number + i, oob,
961235537Sgber		    chip->chip_geom.oob_size, 0);
962235537Sgber		if (err) {
963235537Sgber			device_printf(dev, "%s: cannot allocate OOB\n",
964235537Sgber			    __func__);
965235537Sgber			free(oob, M_NAND);
966235537Sgber			return (ENOMEM);
967235537Sgber		}
968235537Sgber
969235537Sgber		if (!oob[0]) {
970235537Sgber			*bad = 1;
971235537Sgber			free(oob, M_NAND);
972235537Sgber			return (0);
973235537Sgber		}
974235537Sgber	}
975235537Sgber
976235537Sgber	free(oob, M_NAND);
977235537Sgber
978235537Sgber	return (0);
979235537Sgber}
980235537Sgber
981235537Sgberstatic int
982235537Sgbergeneric_get_ecc(device_t dev, void *buf, void *ecc, int *needwrite)
983235537Sgber{
984235537Sgber	struct nand_chip *chip = device_get_softc(dev);
985235537Sgber	struct chip_geom *cg = &chip->chip_geom;
986235537Sgber
987235537Sgber	return (NANDBUS_GET_ECC(device_get_parent(dev), buf, cg->page_size,
988235537Sgber	    ecc, needwrite));
989235537Sgber}
990235537Sgber
991235537Sgberstatic int
992235537Sgbergeneric_correct_ecc(device_t dev, void *buf, void *readecc, void *calcecc)
993235537Sgber{
994235537Sgber	struct nand_chip *chip = device_get_softc(dev);
995235537Sgber	struct chip_geom *cg = &chip->chip_geom;
996235537Sgber
997235537Sgber	return (NANDBUS_CORRECT_ECC(device_get_parent(dev), buf,
998235537Sgber	    cg->page_size, readecc, calcecc));
999235537Sgber}
1000235537Sgber
1001235537Sgber
1002235537Sgber#if 0
1003235537Sgberint
1004235537Sgbernand_chng_read_col(device_t nand, uint32_t col, void *buf, size_t len)
1005235537Sgber{
1006235537Sgber	struct nand_chip *chip;
1007235537Sgber	device_t nandbus;
1008235537Sgber
1009235537Sgber	chip = device_get_softc(nand);
1010235537Sgber	nandbus = device_get_parent(nand);
1011235537Sgber
1012235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL))
1013235537Sgber		return (ENXIO);
1014235537Sgber
1015235537Sgber	if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1))
1016235537Sgber		return (ENXIO);
1017235537Sgber
1018235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END))
1019235537Sgber		return (ENXIO);
1020235537Sgber
1021235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
1022235537Sgber		return (ENXIO);
1023235537Sgber
1024235537Sgber	if (buf != NULL && len > 0)
1025235537Sgber		NANDBUS_READ_BUFFER(nandbus, buf, len);
1026235537Sgber
1027235537Sgber	return (0);
1028235537Sgber}
1029235537Sgber
1030235537Sgberint
1031235537Sgbernand_chng_write_col(device_t dev, uint32_t col, void *buf,
1032235537Sgber    size_t len)
1033235537Sgber{
1034235537Sgber	struct nand_chip *chip;
1035235537Sgber	device_t nandbus;
1036235537Sgber
1037235537Sgber	chip = device_get_softc(dev);
1038235537Sgber	nandbus = device_get_parent(dev);
1039235537Sgber
1040235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL))
1041235537Sgber		return (ENXIO);
1042235537Sgber
1043235537Sgber	if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1))
1044235537Sgber		return (ENXIO);
1045235537Sgber
1046235537Sgber	if (buf != NULL && len > 0)
1047235537Sgber		NANDBUS_WRITE_BUFFER(nandbus, buf, len);
1048235537Sgber
1049235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END))
1050235537Sgber		return (ENXIO);
1051235537Sgber
1052235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
1053235537Sgber		return (ENXIO);
1054235537Sgber
1055235537Sgber	return (0);
1056235537Sgber}
1057235537Sgber
1058235537Sgberint
1059235537Sgbernand_copyback_read(device_t dev, uint32_t page, uint32_t col,
1060235537Sgber    void *buf, size_t len)
1061235537Sgber{
1062235537Sgber	struct nand_chip *chip;
1063235537Sgber	struct page_stat *pg_stat;
1064235537Sgber	device_t nandbus;
1065235537Sgber	uint32_t row;
1066235537Sgber
1067235537Sgber	nand_debug(NDBG_GEN," raw read page %x[%x] at %x", page, col, len);
1068235537Sgber	chip = device_get_softc(dev);
1069235537Sgber	nandbus = device_get_parent(dev);
1070235537Sgber
1071235537Sgber	if (nand_check_page_boundary(chip, page))
1072235537Sgber		return (ENXIO);
1073235537Sgber
1074235537Sgber	page_to_row(&chip->chip_geom, page, &row);
1075235537Sgber
1076235537Sgber	if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_CPBK, row, 0))
1077235537Sgber		return (ENXIO);
1078235537Sgber
1079235537Sgber	DELAY(chip->t_r);
1080235537Sgber	if (check_fail(nandbus))
1081235537Sgber		return (ENXIO);
1082235537Sgber
1083235537Sgber	if (buf != NULL && len > 0)
1084235537Sgber		NANDBUS_READ_BUFFER(nandbus, buf, len);
1085235537Sgber
1086235537Sgber	pg_stat = &(chip->pg_stat[page]);
1087235537Sgber	pg_stat->page_raw_read++;
1088235537Sgber
1089235537Sgber	return (0);
1090235537Sgber}
1091235537Sgber
1092235537Sgberint
1093235537Sgbernand_copyback_prog(device_t dev, uint32_t page, uint32_t col,
1094235537Sgber    void *buf, size_t len)
1095235537Sgber{
1096235537Sgber	struct nand_chip *chip;
1097235537Sgber	struct page_stat *pg_stat;
1098235537Sgber	device_t nandbus;
1099235537Sgber	uint32_t row;
1100235537Sgber
1101235537Sgber	nand_debug(NDBG_GEN,"copyback prog page %x[%x]",  page, len);
1102235537Sgber	chip = device_get_softc(dev);
1103235537Sgber	nandbus = device_get_parent(dev);
1104235537Sgber
1105235537Sgber	if (nand_check_page_boundary(chip, page))
1106235537Sgber		return (ENXIO);
1107235537Sgber
1108235537Sgber	page_to_row(&chip->chip_geom, page, &row);
1109235537Sgber
1110235537Sgber	if (!can_write(nandbus))
1111235537Sgber		return (ENXIO);
1112235537Sgber
1113235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL))
1114235537Sgber		return (ENXIO);
1115235537Sgber
1116235537Sgber	if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1))
1117235537Sgber		return (ENXIO);
1118235537Sgber
1119235537Sgber	if (buf != NULL && len > 0)
1120235537Sgber		NANDBUS_WRITE_BUFFER(nandbus, buf, len);
1121235537Sgber
1122235537Sgber	if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
1123235537Sgber		return (ENXIO);
1124235537Sgber
1125235537Sgber	DELAY(chip->t_prog);
1126235537Sgber
1127235537Sgber	if (check_fail(nandbus))
1128235537Sgber		return (ENXIO);
1129235537Sgber
1130235537Sgber	pg_stat = &(chip->pg_stat[page]);
1131235537Sgber	pg_stat->page_raw_written++;
1132235537Sgber
1133235537Sgber	return (0);
1134235537Sgber}
1135235537Sgber
1136235537Sgberint
1137235537Sgbernand_copyback_prog_intlv(device_t dev, uint32_t page)
1138235537Sgber{
1139235537Sgber	struct nand_chip *chip;
1140235537Sgber	struct page_stat *pg_stat;
1141235537Sgber	device_t nandbus;
1142235537Sgber	uint32_t row;
1143235537Sgber
1144235537Sgber	nand_debug(NDBG_GEN,"cache prog page %x", page);
1145235537Sgber	chip = device_get_softc(dev);
1146235537Sgber	nandbus = device_get_parent(dev);
1147235537Sgber
1148235537Sgber	if (nand_check_page_boundary(chip, page))
1149235537Sgber		return (ENXIO);
1150235537Sgber
1151235537Sgber	page_to_row(&chip->chip_geom, page, &row);
1152235537Sgber
1153235537Sgber	if (!can_write(nandbus))
1154235537Sgber		return (ENXIO);
1155235537Sgber
1156235537Sgber	if (send_start_program_page(nand, row, 0))
1157235537Sgber		return (ENXIO);
1158235537Sgber
1159235537Sgber	if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV))
1160235537Sgber		return (ENXIO);
1161235537Sgber
1162235537Sgber	DELAY(chip->t_prog);
1163235537Sgber
1164235537Sgber	if (check_fail(nandbus))
1165235537Sgber		return (ENXIO);
1166235537Sgber
1167235537Sgber	pg_stat = &(chip->pg_stat[page]);
1168235537Sgber	pg_stat->page_raw_written++;
1169235537Sgber
1170235537Sgber	return (0);
1171235537Sgber}
1172235537Sgber
1173235537Sgberint
1174235537Sgbernand_prog_cache(device_t dev, uint32_t page, uint32_t col,
1175235537Sgber    void *buf, size_t len, uint8_t end)
1176235537Sgber{
1177235537Sgber	struct nand_chip *chip;
1178235537Sgber	struct page_stat *pg_stat;
1179235537Sgber	device_t nandbus;
1180235537Sgber	uint32_t row;
1181235537Sgber	uint8_t command;
1182235537Sgber
1183235537Sgber	nand_debug(NDBG_GEN,"cache prog page %x[%x]",  page, len);
1184235537Sgber	chip = device_get_softc(dev);
1185235537Sgber	nandbus = device_get_parent(dev);
1186235537Sgber
1187235537Sgber	if (nand_check_page_boundary(chip, page))
1188235537Sgber		return (ENXIO);
1189235537Sgber
1190235537Sgber	page_to_row(&chip->chip_geom, page, &row);
1191235537Sgber
1192235537Sgber	if (!can_write(nandbus))
1193235537Sgber		return (ENXIO);
1194235537Sgber
1195235537Sgber	if (send_start_program_page(dev, row, 0))
1196235537Sgber		return (ENXIO);
1197235537Sgber
1198235537Sgber	NANDBUS_WRITE_BUFFER(nandbus, buf, len);
1199235537Sgber
1200235537Sgber	if (end)
1201235537Sgber		command = NAND_CMD_PROG_END;
1202235537Sgber	else
1203235537Sgber		command = NAND_CMD_PROG_CACHE;
1204235537Sgber
1205235537Sgber	if (send_end_program_page(nandbus, command))
1206235537Sgber		return (ENXIO);
1207235537Sgber
1208235537Sgber	DELAY(chip->t_prog);
1209235537Sgber
1210235537Sgber	if (check_fail(nandbus))
1211235537Sgber		return (ENXIO);
1212235537Sgber
1213235537Sgber	pg_stat = &(chip->pg_stat[page]);
1214235537Sgber	pg_stat->page_raw_written++;
1215235537Sgber
1216235537Sgber	return (0);
1217235537Sgber}
1218235537Sgber
1219235537Sgberint
1220235537Sgbernand_read_cache(device_t dev, uint32_t page, uint32_t col,
1221235537Sgber    void *buf, size_t len, uint8_t end)
1222235537Sgber{
1223235537Sgber	struct nand_chip *chip;
1224235537Sgber	struct page_stat *pg_stat;
1225235537Sgber	device_t nandbus;
1226235537Sgber	uint32_t row;
1227235537Sgber	uint8_t command;
1228235537Sgber
1229235537Sgber	nand_debug(NDBG_GEN,"cache read page %x[%x] ", page, len);
1230235537Sgber	chip = device_get_softc(dev);
1231235537Sgber	nandbus = device_get_parent(dev);
1232235537Sgber
1233235537Sgber	if (nand_check_page_boundary(chip, page))
1234235537Sgber		return (ENXIO);
1235235537Sgber
1236235537Sgber	page_to_row(&chip->chip_geom, page, &row);
1237235537Sgber
1238235537Sgber	if (page != -1) {
1239235537Sgber		if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ))
1240235537Sgber			return (ENXIO);
1241235537Sgber
1242235537Sgber		if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1))
1243235537Sgber			return (ENXIO);
1244235537Sgber	}
1245235537Sgber
1246235537Sgber	if (end)
1247235537Sgber		command = NAND_CMD_READ_CACHE_END;
1248235537Sgber	else
1249235537Sgber		command = NAND_CMD_READ_CACHE;
1250235537Sgber
1251235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, command))
1252235537Sgber		return (ENXIO);
1253235537Sgber
1254235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
1255235537Sgber		return (ENXIO);
1256235537Sgber
1257235537Sgber	DELAY(chip->t_r);
1258235537Sgber	if (check_fail(nandbus))
1259235537Sgber		return (ENXIO);
1260235537Sgber
1261235537Sgber	if (buf != NULL && len > 0)
1262235537Sgber		NANDBUS_READ_BUFFER(nandbus, buf, len);
1263235537Sgber
1264235537Sgber	pg_stat = &(chip->pg_stat[page]);
1265235537Sgber	pg_stat->page_raw_read++;
1266235537Sgber
1267235537Sgber	return (0);
1268235537Sgber}
1269235537Sgber
1270235537Sgberint
1271235537Sgbernand_get_feature(device_t dev, uint8_t feat, void *buf)
1272235537Sgber{
1273235537Sgber	struct nand_chip *chip;
1274235537Sgber	device_t nandbus;
1275235537Sgber
1276235537Sgber	nand_debug(NDBG_GEN,"nand get feature");
1277235537Sgber
1278235537Sgber	chip = device_get_softc(dev);
1279235537Sgber	nandbus = device_get_parent(dev);
1280235537Sgber
1281235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_GET_FEATURE))
1282235537Sgber		return (ENXIO);
1283235537Sgber
1284235537Sgber	if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat))
1285235537Sgber		return (ENXIO);
1286235537Sgber
1287235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
1288235537Sgber		return (ENXIO);
1289235537Sgber
1290235537Sgber	DELAY(chip->t_r);
1291235537Sgber	NANDBUS_READ_BUFFER(nandbus, buf, 4);
1292235537Sgber
1293235537Sgber	return (0);
1294235537Sgber}
1295235537Sgber
1296235537Sgberint
1297235537Sgbernand_set_feature(device_t dev, uint8_t feat, void *buf)
1298235537Sgber{
1299235537Sgber	struct nand_chip *chip;
1300235537Sgber	device_t nandbus;
1301235537Sgber
1302235537Sgber	nand_debug(NDBG_GEN,"nand set feature");
1303235537Sgber
1304235537Sgber	chip = device_get_softc(dev);
1305235537Sgber	nandbus = device_get_parent(dev);
1306235537Sgber
1307235537Sgber	if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SET_FEATURE))
1308235537Sgber		return (ENXIO);
1309235537Sgber
1310235537Sgber	if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat))
1311235537Sgber		return (ENXIO);
1312235537Sgber
1313235537Sgber	NANDBUS_WRITE_BUFFER(nandbus, buf, 4);
1314235537Sgber
1315235537Sgber	if (NANDBUS_START_COMMAND(nandbus))
1316235537Sgber		return (ENXIO);
1317235537Sgber
1318235537Sgber	return (0);
1319235537Sgber}
1320235537Sgber#endif
1321