1238046Smarcel/*-
2238046Smarcel * Copyright (C) 2012 Juniper Networks, Inc.
3238046Smarcel * Copyright (C) 2009-2012 Semihalf
4238046Smarcel * All rights reserved.
5238046Smarcel *
6238046Smarcel * Redistribution and use in source and binary forms, with or without
7238046Smarcel * modification, are permitted provided that the following conditions
8238046Smarcel * are met:
9238046Smarcel * 1. Redistributions of source code must retain the above copyright
10238046Smarcel *    notice, this list of conditions and the following disclaimer.
11238046Smarcel * 2. Redistributions in binary form must reproduce the above copyright
12238046Smarcel *    notice, this list of conditions and the following disclaimer in the
13238046Smarcel *    documentation and/or other materials provided with the distribution.
14238046Smarcel *
15238046Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16238046Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17238046Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18238046Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19238046Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20238046Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21238046Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22238046Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23238046Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24238046Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25238046Smarcel * SUCH DAMAGE.
26238046Smarcel */
27238046Smarcel/*
28238046Smarcel * TODO :
29238046Smarcel *
30238046Smarcel *  -- test support for small pages
31238046Smarcel *  -- support for reading ONFI parameters
32238046Smarcel *  -- support for cached and interleaving commands
33238046Smarcel *  -- proper setting of AL bits in FMR
34238046Smarcel */
35238046Smarcel
36238046Smarcel#include <sys/cdefs.h>
37238046Smarcel__FBSDID("$FreeBSD$");
38238046Smarcel
39238046Smarcel#include <sys/param.h>
40238046Smarcel#include <sys/systm.h>
41238046Smarcel#include <sys/proc.h>
42238046Smarcel#include <sys/bus.h>
43238046Smarcel#include <sys/conf.h>
44238046Smarcel#include <sys/kernel.h>
45238046Smarcel#include <sys/module.h>
46238046Smarcel#include <sys/malloc.h>
47238046Smarcel#include <sys/rman.h>
48238046Smarcel#include <sys/sysctl.h>
49238046Smarcel#include <sys/time.h>
50238046Smarcel#include <sys/kdb.h>
51238046Smarcel
52238046Smarcel#include <machine/bus.h>
53238046Smarcel#include <machine/fdt.h>
54238046Smarcel
55238046Smarcel#include <dev/ofw/ofw_bus.h>
56238046Smarcel#include <dev/ofw/ofw_bus_subr.h>
57238046Smarcel
58238046Smarcel#include <powerpc/mpc85xx/lbc.h>
59238046Smarcel
60238046Smarcel#include <dev/nand/nand.h>
61238046Smarcel#include <dev/nand/nandbus.h>
62238046Smarcel
63238046Smarcel#include "nfc_fsl.h"
64238046Smarcel
65238046Smarcel#include "nfc_if.h"
66238046Smarcel
67238046Smarcel#define LBC_READ(regname)	lbc_read_reg(dev, (LBC85XX_ ## regname))
68238046Smarcel#define LBC_WRITE(regname, val)	lbc_write_reg(dev, (LBC85XX_ ## regname), val)
69238046Smarcel
70238046Smarcelenum addr_type {
71238046Smarcel	ADDR_NONE,
72238046Smarcel	ADDR_ID,
73238046Smarcel	ADDR_ROW,
74238046Smarcel	ADDR_ROWCOL
75238046Smarcel};
76238046Smarcel
77238046Smarcelstruct fsl_nfc_fcm {
78238046Smarcel	/* Read-only after initialization */
79238046Smarcel	uint32_t	reg_fmr;
80238046Smarcel
81238046Smarcel	/* To be preserved across "start_command" */
82238046Smarcel	u_int		buf_ofs;
83238046Smarcel	u_int		read_ptr;
84238046Smarcel	u_int		status:1;
85238046Smarcel
86238046Smarcel	/* Command state -- cleared by "start_command" */
87238046Smarcel	uint32_t	fcm_startzero;
88238046Smarcel	uint32_t	reg_fcr;
89238046Smarcel	uint32_t	reg_fir;
90238046Smarcel	uint32_t	reg_mdr;
91238046Smarcel	uint32_t	reg_fbcr;
92238046Smarcel	uint32_t	reg_fbar;
93238046Smarcel	uint32_t	reg_fpar;
94238046Smarcel	u_int		cmdnr;
95238046Smarcel	u_int		opnr;
96238046Smarcel	u_int		pg_ofs;
97238046Smarcel	enum addr_type	addr_type;
98238046Smarcel	u_int		addr_bytes;
99238046Smarcel	u_int		row_addr;
100238046Smarcel	u_int		column_addr;
101238046Smarcel	u_int		data_fir:8;
102238046Smarcel	uint32_t	fcm_endzero;
103238046Smarcel};
104238046Smarcel
105238046Smarcelstruct fsl_nand_softc {
106238046Smarcel	struct nand_softc		nand_dev;
107238046Smarcel	device_t			dev;
108238046Smarcel	struct resource			*res;
109238046Smarcel	int				rid;		/* Resourceid */
110238046Smarcel	struct lbc_devinfo		*dinfo;
111238046Smarcel	struct fsl_nfc_fcm		fcm;
112238046Smarcel	uint8_t				col_cycles;
113238046Smarcel	uint8_t				row_cycles;
114238046Smarcel	uint16_t			pgsz;		/* Page size */
115238046Smarcel};
116238046Smarcel
117238046Smarcelstatic int	fsl_nand_attach(device_t dev);
118238046Smarcelstatic int	fsl_nand_probe(device_t dev);
119238046Smarcelstatic int	fsl_nand_detach(device_t dev);
120238046Smarcel
121238046Smarcelstatic int	fsl_nfc_select_cs(device_t dev, uint8_t cs);
122238046Smarcelstatic int	fsl_nfc_read_rnb(device_t dev);
123238046Smarcelstatic int	fsl_nfc_send_command(device_t dev, uint8_t command);
124238046Smarcelstatic int	fsl_nfc_send_address(device_t dev, uint8_t address);
125238046Smarcelstatic uint8_t	fsl_nfc_read_byte(device_t dev);
126238046Smarcelstatic int	fsl_nfc_start_command(device_t dev);
127238046Smarcelstatic void	fsl_nfc_read_buf(device_t dev, void *buf, uint32_t len);
128238046Smarcelstatic void	fsl_nfc_write_buf(device_t dev, void *buf, uint32_t len);
129238046Smarcel
130238046Smarcelstatic device_method_t fsl_nand_methods[] = {
131238046Smarcel	DEVMETHOD(device_probe,		fsl_nand_probe),
132238046Smarcel	DEVMETHOD(device_attach,	fsl_nand_attach),
133238046Smarcel	DEVMETHOD(device_detach,	fsl_nand_detach),
134238046Smarcel
135238046Smarcel	DEVMETHOD(nfc_select_cs,	fsl_nfc_select_cs),
136238046Smarcel	DEVMETHOD(nfc_read_rnb,		fsl_nfc_read_rnb),
137238046Smarcel	DEVMETHOD(nfc_start_command,	fsl_nfc_start_command),
138238046Smarcel	DEVMETHOD(nfc_send_command,	fsl_nfc_send_command),
139238046Smarcel	DEVMETHOD(nfc_send_address,	fsl_nfc_send_address),
140238046Smarcel	DEVMETHOD(nfc_read_byte,	fsl_nfc_read_byte),
141238046Smarcel	DEVMETHOD(nfc_read_buf,		fsl_nfc_read_buf),
142238046Smarcel	DEVMETHOD(nfc_write_buf,	fsl_nfc_write_buf),
143238046Smarcel	{ 0, 0 },
144238046Smarcel};
145238046Smarcel
146238046Smarcelstatic driver_t fsl_nand_driver = {
147238046Smarcel	"nand",
148238046Smarcel	fsl_nand_methods,
149238046Smarcel	sizeof(struct fsl_nand_softc),
150238046Smarcel};
151238046Smarcel
152238046Smarcelstatic devclass_t fsl_nand_devclass;
153238046Smarcel
154238046SmarcelDRIVER_MODULE(fsl_nand, lbc, fsl_nand_driver, fsl_nand_devclass,
155238046Smarcel    0, 0);
156238046Smarcel
157238046Smarcelstatic int fsl_nand_build_address(device_t dev, uint32_t page, uint32_t column);
158238046Smarcelstatic int fsl_nand_chip_preprobe(device_t dev, struct nand_id *id);
159238046Smarcel
160238046Smarcel#ifdef NAND_DEBUG_TIMING
161238046Smarcelstatic device_t fcm_devs[8];
162238046Smarcel#endif
163238046Smarcel
164238046Smarcel#define CMD_SHIFT(cmd_num)	(24 - ((cmd_num) * 8))
165238046Smarcel#define OP_SHIFT(op_num)	(28 - ((op_num) * 4))
166238046Smarcel
167238046Smarcel#define FSL_LARGE_PAGE_SIZE	(2112)
168238046Smarcel#define FSL_SMALL_PAGE_SIZE	(528)
169238046Smarcel
170238046Smarcelstatic void
171238046Smarcelfsl_nand_init_regs(struct fsl_nand_softc *sc)
172238046Smarcel{
173238046Smarcel	uint32_t or_v, br_v;
174238046Smarcel	device_t dev;
175238046Smarcel
176238046Smarcel	dev = sc->dev;
177238046Smarcel
178238046Smarcel	sc->fcm.reg_fmr = (15 << FMR_CWTO_SHIFT);
179238046Smarcel
180238046Smarcel	/*
181238046Smarcel	 * Setup 4 row cycles and hope that chip ignores superfluous address
182238046Smarcel	 * bytes.
183238046Smarcel	 */
184238046Smarcel	sc->fcm.reg_fmr |= (2 << FMR_AL_SHIFT);
185238046Smarcel
186238046Smarcel	/* Reprogram BR(x) */
187238046Smarcel	br_v = lbc_read_reg(dev, LBC85XX_BR(sc->dinfo->di_bank));
188238046Smarcel	br_v &= 0xffff8000;
189238046Smarcel	br_v |= 1 << 11;	/* 8-bit port size */
190238046Smarcel	br_v |= 0 << 9;		/* No ECC checking and generation */
191238046Smarcel	br_v |= 1 << 5;		/* FCM machine */
192238046Smarcel	br_v |= 1;		/* Valid */
193238046Smarcel	lbc_write_reg(dev, LBC85XX_BR(sc->dinfo->di_bank), br_v);
194238046Smarcel
195238046Smarcel	/* Reprogram OR(x) */
196238046Smarcel	or_v = lbc_read_reg(dev, LBC85XX_OR(sc->dinfo->di_bank));
197238046Smarcel	or_v &= 0xfffffc00;
198238046Smarcel	or_v |= 0x03AE;		/* Default POR timing */
199238046Smarcel	lbc_write_reg(dev, LBC85XX_OR(sc->dinfo->di_bank), or_v);
200238046Smarcel
201238046Smarcel	if (or_v & OR_FCM_PAGESIZE) {
202238046Smarcel		sc->pgsz = FSL_LARGE_PAGE_SIZE;
203238046Smarcel		sc->col_cycles = 2;
204238046Smarcel		nand_debug(NDBG_DRV, "%s: large page NAND device at #%d",
205238046Smarcel		    device_get_nameunit(dev), sc->dinfo->di_bank);
206238046Smarcel	} else {
207238046Smarcel		sc->pgsz = FSL_SMALL_PAGE_SIZE;
208238046Smarcel		sc->col_cycles = 1;
209238046Smarcel		nand_debug(NDBG_DRV, "%s: small page NAND device at #%d",
210238046Smarcel		    device_get_nameunit(dev), sc->dinfo->di_bank);
211238046Smarcel	}
212238046Smarcel}
213238046Smarcel
214238046Smarcelstatic int
215238046Smarcelfsl_nand_probe(device_t dev)
216238046Smarcel{
217238046Smarcel
218238046Smarcel	if (!ofw_bus_is_compatible(dev, "fsl,elbc-fcm-nand"))
219238046Smarcel		return (ENXIO);
220238046Smarcel
221238046Smarcel	device_set_desc(dev, "Freescale localbus FCM Controller");
222238046Smarcel	return (BUS_PROBE_DEFAULT);
223238046Smarcel}
224238046Smarcel
225238046Smarcelstatic int
226238046Smarcelfsl_nand_attach(device_t dev)
227238046Smarcel{
228238046Smarcel	struct fsl_nand_softc *sc;
229238046Smarcel	struct nand_id id;
230238046Smarcel	struct nand_params *param;
231238046Smarcel	uint32_t num_pages;
232238046Smarcel
233238046Smarcel	sc = device_get_softc(dev);
234238046Smarcel	sc->dev = dev;
235238046Smarcel	sc->dinfo = device_get_ivars(dev);
236238046Smarcel
237238046Smarcel	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
238238046Smarcel	    RF_ACTIVE);
239238046Smarcel	if (sc->res == NULL) {
240238046Smarcel		device_printf(dev, "could not allocate resources!\n");
241238046Smarcel		return (ENXIO);
242238046Smarcel	}
243238046Smarcel
244238046Smarcel	bzero(&sc->fcm, sizeof(sc->fcm));
245238046Smarcel
246238046Smarcel	/* Init register and check if HW ECC turned on */
247238046Smarcel	fsl_nand_init_regs(sc);
248238046Smarcel
249238046Smarcel	/* Chip is probed, so determine number of row address cycles */
250238046Smarcel	fsl_nand_chip_preprobe(dev, &id);
251238046Smarcel	param = nand_get_params(&id);
252238046Smarcel	if (param != NULL) {
253238046Smarcel		num_pages = (param->chip_size << 20) / param->page_size;
254238046Smarcel		while(num_pages) {
255238046Smarcel			sc->row_cycles++;
256238046Smarcel			num_pages >>= 8;
257238046Smarcel		}
258238046Smarcel
259238046Smarcel		sc->fcm.reg_fmr &= ~(FMR_AL);
260238046Smarcel		sc->fcm.reg_fmr |= (sc->row_cycles - 2) << FMR_AL_SHIFT;
261238046Smarcel	}
262238046Smarcel
263238046Smarcel	nand_init(&sc->nand_dev, dev, NAND_ECC_SOFT, 0, 0, NULL, NULL);
264238046Smarcel
265238046Smarcel#ifdef NAND_DEBUG_TIMING
266238046Smarcel	fcm_devs[sc->dinfo->di_bank] = dev;
267238046Smarcel#endif
268238046Smarcel
269238046Smarcel	return (nandbus_create(dev));
270238046Smarcel}
271238046Smarcel
272238046Smarcelstatic int
273238046Smarcelfsl_nand_detach(device_t dev)
274238046Smarcel{
275238046Smarcel	struct fsl_nand_softc *sc;
276238046Smarcel
277238046Smarcel	sc = device_get_softc(dev);
278238046Smarcel
279238046Smarcel	if (sc->res != NULL)
280238046Smarcel		bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
281238046Smarcel
282238046Smarcel	return (0);
283238046Smarcel}
284238046Smarcel
285238046Smarcelstatic int
286238046Smarcelfsl_nfc_select_cs(device_t dev, uint8_t cs)
287238046Smarcel{
288238046Smarcel
289238046Smarcel	// device_printf(dev, "%s(cs=%u)\n", __func__, cs);
290238046Smarcel	return ((cs > 0) ? EINVAL : 0);
291238046Smarcel}
292238046Smarcel
293238046Smarcelstatic int
294238046Smarcelfsl_nfc_read_rnb(device_t dev)
295238046Smarcel{
296238046Smarcel
297238046Smarcel	// device_printf(dev, "%s()\n", __func__);
298238046Smarcel	return (0);
299238046Smarcel}
300238046Smarcel
301238046Smarcelstatic int
302238046Smarcelfsl_nfc_send_command(device_t dev, uint8_t command)
303238046Smarcel{
304238046Smarcel	struct fsl_nand_softc *sc;
305238046Smarcel	struct fsl_nfc_fcm *fcm;
306238046Smarcel	uint8_t	fir_op;
307238046Smarcel
308238046Smarcel	// device_printf(dev, "%s(command=%u)\n", __func__, command);
309238046Smarcel
310238046Smarcel	sc = device_get_softc(dev);
311238046Smarcel	fcm = &sc->fcm;
312238046Smarcel
313238046Smarcel	if (command == NAND_CMD_PROG_END) {
314238046Smarcel		fcm->reg_fir |= (FIR_OP_WB << OP_SHIFT(fcm->opnr));
315238046Smarcel		fcm->opnr++;
316238046Smarcel	}
317238046Smarcel	fcm->reg_fcr |= command << CMD_SHIFT(fcm->cmdnr);
318238046Smarcel	fir_op = (fcm->cmdnr == 0) ? FIR_OP_CW0 : FIR_OP_CM(fcm->cmdnr);
319238046Smarcel	fcm->cmdnr++;
320238046Smarcel
321238046Smarcel	fcm->reg_fir |= (fir_op << OP_SHIFT(fcm->opnr));
322238046Smarcel	fcm->opnr++;
323238046Smarcel
324238046Smarcel	switch (command) {
325238046Smarcel	case NAND_CMD_READ_ID:
326238046Smarcel		fcm->data_fir = FIR_OP_RBW;
327238046Smarcel		fcm->addr_type = ADDR_ID;
328238046Smarcel		break;
329238046Smarcel	case NAND_CMD_SMALLOOB:
330238046Smarcel		fcm->pg_ofs += 256;
331238046Smarcel		/*FALLTHROUGH*/
332238046Smarcel	case NAND_CMD_SMALLB:
333238046Smarcel		fcm->pg_ofs += 256;
334238046Smarcel		/*FALLTHROUGH*/
335238046Smarcel	case NAND_CMD_READ: /* NAND_CMD_SMALLA */
336238046Smarcel		fcm->data_fir = FIR_OP_RBW;
337238046Smarcel		fcm->addr_type = ADDR_ROWCOL;
338238046Smarcel		break;
339238046Smarcel	case NAND_CMD_STATUS:
340238046Smarcel		fcm->data_fir = FIR_OP_RS;
341238046Smarcel		fcm->status = 1;
342238046Smarcel		break;
343238046Smarcel	case NAND_CMD_ERASE:
344238046Smarcel		fcm->addr_type = ADDR_ROW;
345238046Smarcel		break;
346238046Smarcel	case NAND_CMD_PROG:
347238046Smarcel		fcm->addr_type = ADDR_ROWCOL;
348238046Smarcel		break;
349238046Smarcel	}
350238046Smarcel	return (0);
351238046Smarcel}
352238046Smarcel
353238046Smarcelstatic int
354238046Smarcelfsl_nfc_send_address(device_t dev, uint8_t addr)
355238046Smarcel{
356238046Smarcel	struct fsl_nand_softc *sc;
357238046Smarcel	struct fsl_nfc_fcm *fcm;
358238046Smarcel	uint32_t addr_bits;
359238046Smarcel
360238046Smarcel	// device_printf(dev, "%s(address=%u)\n", __func__, addr);
361238046Smarcel
362238046Smarcel	sc = device_get_softc(dev);
363238046Smarcel	fcm = &sc->fcm;
364238046Smarcel
365238046Smarcel	KASSERT(fcm->addr_type != ADDR_NONE,
366238046Smarcel	    ("controller doesn't expect address cycle"));
367238046Smarcel
368238046Smarcel	addr_bits = addr;
369238046Smarcel
370238046Smarcel	if (fcm->addr_type == ADDR_ID) {
371238046Smarcel		fcm->reg_fir |= (FIR_OP_UA << OP_SHIFT(fcm->opnr));
372238046Smarcel		fcm->opnr++;
373238046Smarcel
374238046Smarcel		fcm->reg_fbcr = 5;
375238046Smarcel		fcm->reg_fbar = 0;
376238046Smarcel		fcm->reg_fpar = 0;
377238046Smarcel		fcm->reg_mdr = addr_bits;
378238046Smarcel		fcm->buf_ofs = 0;
379238046Smarcel		fcm->read_ptr = 0;
380238046Smarcel		return (0);
381238046Smarcel	}
382238046Smarcel
383238046Smarcel	if (fcm->addr_type == ADDR_ROW) {
384238046Smarcel		addr_bits <<= fcm->addr_bytes * 8;
385238046Smarcel		fcm->row_addr |= addr_bits;
386238046Smarcel		fcm->addr_bytes++;
387238046Smarcel		if (fcm->addr_bytes < sc->row_cycles)
388238046Smarcel			return (0);
389238046Smarcel	} else {
390238046Smarcel		if (fcm->addr_bytes < sc->col_cycles) {
391238046Smarcel			addr_bits <<= fcm->addr_bytes * 8;
392238046Smarcel			fcm->column_addr |= addr_bits;
393238046Smarcel		} else {
394238046Smarcel			addr_bits <<= (fcm->addr_bytes - sc->col_cycles) * 8;
395238046Smarcel			fcm->row_addr |= addr_bits;
396238046Smarcel		}
397238046Smarcel		fcm->addr_bytes++;
398238046Smarcel		if (fcm->addr_bytes < (sc->row_cycles + sc->col_cycles))
399238046Smarcel			return (0);
400238046Smarcel	}
401238046Smarcel
402238046Smarcel	return (fsl_nand_build_address(dev, fcm->row_addr, fcm->column_addr));
403238046Smarcel}
404238046Smarcel
405238046Smarcelstatic int
406238046Smarcelfsl_nand_build_address(device_t dev, uint32_t row, uint32_t column)
407238046Smarcel{
408238046Smarcel	struct fsl_nand_softc *sc;
409238046Smarcel	struct fsl_nfc_fcm *fcm;
410238046Smarcel	uint32_t byte_count = 0;
411238046Smarcel	uint32_t block_address = 0;
412238046Smarcel	uint32_t page_address = 0;
413238046Smarcel
414238046Smarcel	sc = device_get_softc(dev);
415238046Smarcel	fcm = &sc->fcm;
416238046Smarcel
417238046Smarcel	fcm->read_ptr = 0;
418238046Smarcel	fcm->buf_ofs = 0;
419238046Smarcel
420238046Smarcel	if (fcm->addr_type == ADDR_ROWCOL) {
421238046Smarcel		fcm->reg_fir |= (FIR_OP_CA << OP_SHIFT(fcm->opnr));
422238046Smarcel		fcm->opnr++;
423238046Smarcel
424238046Smarcel		column += fcm->pg_ofs;
425238046Smarcel		fcm->pg_ofs = 0;
426238046Smarcel
427238046Smarcel		page_address |= column;
428238046Smarcel
429238046Smarcel		if (column != 0) {
430238046Smarcel			byte_count = sc->pgsz - column;
431238046Smarcel			fcm->read_ptr = column;
432238046Smarcel		}
433238046Smarcel	}
434238046Smarcel
435238046Smarcel	fcm->reg_fir |= (FIR_OP_PA << OP_SHIFT(fcm->opnr));
436238046Smarcel	fcm->opnr++;
437238046Smarcel
438238046Smarcel	if (sc->pgsz == FSL_LARGE_PAGE_SIZE) {
439238046Smarcel		block_address = row >> 6;
440238046Smarcel		page_address |= ((row << FPAR_LP_PI_SHIFT) & FPAR_LP_PI);
441238046Smarcel		fcm->buf_ofs = (row & 1) * 4096;
442238046Smarcel	} else {
443238046Smarcel		block_address = row >> 5;
444238046Smarcel		page_address |= ((row << FPAR_SP_PI_SHIFT) & FPAR_SP_PI);
445238046Smarcel		fcm->buf_ofs = (row & 7) * 1024;
446238046Smarcel	}
447238046Smarcel
448238046Smarcel	fcm->reg_fbcr = byte_count;
449238046Smarcel	fcm->reg_fbar = block_address;
450238046Smarcel	fcm->reg_fpar = page_address;
451238046Smarcel	return (0);
452238046Smarcel}
453238046Smarcel
454238046Smarcelstatic int
455238046Smarcelfsl_nfc_start_command(device_t dev)
456238046Smarcel{
457238046Smarcel	struct fsl_nand_softc *sc;
458238046Smarcel	struct fsl_nfc_fcm *fcm;
459238046Smarcel	uint32_t fmr, ltesr_v;
460238046Smarcel	int error, timeout;
461238046Smarcel
462238046Smarcel	// device_printf(dev, "%s()\n", __func__);
463238046Smarcel
464238046Smarcel	sc = device_get_softc(dev);
465238046Smarcel	fcm = &sc->fcm;
466238046Smarcel
467238046Smarcel	fmr = fcm->reg_fmr | FMR_OP;
468238046Smarcel
469238046Smarcel	if (fcm->data_fir)
470238046Smarcel		fcm->reg_fir |= (fcm->data_fir << OP_SHIFT(fcm->opnr));
471238046Smarcel
472238046Smarcel	LBC_WRITE(FIR, fcm->reg_fir);
473238046Smarcel	LBC_WRITE(FCR, fcm->reg_fcr);
474238046Smarcel
475238046Smarcel	LBC_WRITE(FMR, fmr);
476238046Smarcel
477238046Smarcel	LBC_WRITE(FBCR, fcm->reg_fbcr);
478238046Smarcel	LBC_WRITE(FBAR, fcm->reg_fbar);
479238046Smarcel	LBC_WRITE(FPAR, fcm->reg_fpar);
480238046Smarcel
481238046Smarcel	if (fcm->addr_type == ADDR_ID)
482238046Smarcel		LBC_WRITE(MDR, fcm->reg_mdr);
483238046Smarcel
484238046Smarcel	nand_debug(NDBG_DRV, "BEFORE:\nFMR=%#x, FIR=%#x, FCR=%#x", fmr,
485238046Smarcel	    fcm->reg_fir, fcm->reg_fcr);
486238046Smarcel	nand_debug(NDBG_DRV, "MDR=%#x, FBAR=%#x, FPAR=%#x, FBCR=%#x",
487238046Smarcel	    LBC_READ(MDR), fcm->reg_fbar, fcm->reg_fpar, fcm->reg_fbcr);
488238046Smarcel
489238046Smarcel	LBC_WRITE(LSOR, sc->dinfo->di_bank);
490238046Smarcel
491238046Smarcel	timeout = (cold) ? FSL_FCM_WAIT_TIMEOUT : ~0;
492238046Smarcel	error = 0;
493238046Smarcel	ltesr_v = LBC_READ(LTESR);
494238046Smarcel	while (!error && (ltesr_v & LTESR_CC) == 0) {
495238046Smarcel		if (cold) {
496238046Smarcel			DELAY(1000);
497238046Smarcel			timeout--;
498238046Smarcel			if (timeout < 0)
499238046Smarcel				error = EWOULDBLOCK;
500238046Smarcel		} else
501238046Smarcel			error = tsleep(device_get_parent(sc->dev), PRIBIO,
502238046Smarcel			    "nfcfsl", hz);
503238046Smarcel		ltesr_v = LBC_READ(LTESR);
504238046Smarcel	}
505238046Smarcel	if (error)
506238046Smarcel		nand_debug(NDBG_DRV, "Command complete wait timeout\n");
507238046Smarcel
508238046Smarcel	nand_debug(NDBG_DRV, "AFTER:\nLTESR=%#x, LTEDR=%#x, LTEIR=%#x,"
509238046Smarcel	    " LTEATR=%#x, LTEAR=%#x, LTECCR=%#x", ltesr_v,
510238046Smarcel	    LBC_READ(LTEDR), LBC_READ(LTEIR), LBC_READ(LTEATR),
511238046Smarcel	    LBC_READ(LTEAR), LBC_READ(LTECCR));
512238046Smarcel
513238046Smarcel	bzero(&fcm->fcm_startzero,
514238046Smarcel	    __rangeof(struct fsl_nfc_fcm, fcm_startzero, fcm_endzero));
515238046Smarcel
516238046Smarcel	if (fcm->status)
517238046Smarcel		sc->fcm.reg_mdr = LBC_READ(MDR);
518238046Smarcel
519238046Smarcel	/* Even if timeout occured, we should perform steps below */
520238046Smarcel	LBC_WRITE(LTESR, ltesr_v);
521238046Smarcel	LBC_WRITE(LTEATR, 0);
522238046Smarcel
523238046Smarcel	return (error);
524238046Smarcel}
525238046Smarcel
526238046Smarcelstatic uint8_t
527238046Smarcelfsl_nfc_read_byte(device_t dev)
528238046Smarcel{
529238046Smarcel	struct fsl_nand_softc *sc = device_get_softc(dev);
530238046Smarcel	uint32_t offset;
531238046Smarcel
532238046Smarcel	// device_printf(dev, "%s()\n", __func__);
533238046Smarcel
534238046Smarcel	/*
535238046Smarcel	 * LBC controller allows us to read status into a MDR instead of FCM
536238046Smarcel	 * buffer. If last operation requested before read_byte() was STATUS,
537238046Smarcel	 * then return MDR instead of reading a single byte from a buffer.
538238046Smarcel	 */
539238046Smarcel	if (sc->fcm.status) {
540238046Smarcel		sc->fcm.status = 0;
541238046Smarcel		return (sc->fcm.reg_mdr);
542238046Smarcel	}
543238046Smarcel
544238046Smarcel	KASSERT(sc->fcm.read_ptr < sc->pgsz,
545238046Smarcel	    ("Attempt to read beyond buffer %x %x", sc->fcm.read_ptr,
546238046Smarcel	    sc->pgsz));
547238046Smarcel
548238046Smarcel	offset = sc->fcm.buf_ofs + sc->fcm.read_ptr;
549238046Smarcel	sc->fcm.read_ptr++;
550238046Smarcel	return (bus_read_1(sc->res, offset));
551238046Smarcel}
552238046Smarcel
553238046Smarcelstatic void
554238046Smarcelfsl_nfc_read_buf(device_t dev, void *buf, uint32_t len)
555238046Smarcel{
556238046Smarcel	struct fsl_nand_softc *sc = device_get_softc(dev);
557238046Smarcel	uint32_t offset;
558238046Smarcel	int bytesleft = 0;
559238046Smarcel
560238046Smarcel	// device_printf(dev, "%s(buf=%p, len=%u)\n", __func__, buf, len);
561238046Smarcel
562238046Smarcel	nand_debug(NDBG_DRV, "REQUEST OF 0x%0x B (BIB=0x%0x, NTR=0x%0x)",
563238046Smarcel	    len, sc->pgsz, sc->fcm.read_ptr);
564238046Smarcel
565238046Smarcel	bytesleft = MIN((unsigned int)len, sc->pgsz - sc->fcm.read_ptr);
566238046Smarcel
567238046Smarcel	offset = sc->fcm.buf_ofs + sc->fcm.read_ptr;
568238046Smarcel	bus_read_region_1(sc->res, offset, buf, bytesleft);
569238046Smarcel	sc->fcm.read_ptr += bytesleft;
570238046Smarcel}
571238046Smarcel
572238046Smarcelstatic void
573238046Smarcelfsl_nfc_write_buf(device_t dev, void *buf, uint32_t len)
574238046Smarcel{
575238046Smarcel	struct fsl_nand_softc *sc = device_get_softc(dev);
576238046Smarcel	uint32_t offset;
577238046Smarcel	int bytesleft = 0;
578238046Smarcel
579238046Smarcel	// device_printf(dev, "%s(buf=%p, len=%u)\n", __func__, buf, len);
580238046Smarcel
581238046Smarcel	KASSERT(len <= sc->pgsz - sc->fcm.read_ptr,
582238046Smarcel	    ("Attempt to write beyond buffer"));
583238046Smarcel
584238046Smarcel	bytesleft = MIN((unsigned int)len, sc->pgsz - sc->fcm.read_ptr);
585238046Smarcel
586238046Smarcel	nand_debug(NDBG_DRV, "REQUEST TO WRITE 0x%0x (BIB=0x%0x, NTR=0x%0x)",
587238046Smarcel	    bytesleft, sc->pgsz, sc->fcm.read_ptr);
588238046Smarcel
589238046Smarcel	offset = sc->fcm.buf_ofs + sc->fcm.read_ptr;
590238046Smarcel	bus_write_region_1(sc->res, offset, buf, bytesleft);
591238046Smarcel	sc->fcm.read_ptr += bytesleft;
592238046Smarcel}
593238046Smarcel
594238046Smarcelstatic int
595238046Smarcelfsl_nand_chip_preprobe(device_t dev, struct nand_id *id)
596238046Smarcel{
597238046Smarcel
598238046Smarcel	if (fsl_nfc_send_command(dev, NAND_CMD_RESET) != 0)
599238046Smarcel		return (ENXIO);
600238046Smarcel
601238046Smarcel	if (fsl_nfc_start_command(dev) != 0)
602238046Smarcel		return (ENXIO);
603238046Smarcel
604238046Smarcel	DELAY(1000);
605238046Smarcel
606238046Smarcel	if (fsl_nfc_send_command(dev, NAND_CMD_READ_ID))
607238046Smarcel		return (ENXIO);
608238046Smarcel
609238046Smarcel	if (fsl_nfc_send_address(dev, 0))
610238046Smarcel		return (ENXIO);
611238046Smarcel
612238046Smarcel	if (fsl_nfc_start_command(dev) != 0)
613238046Smarcel		return (ENXIO);
614238046Smarcel
615238046Smarcel	DELAY(25);
616238046Smarcel
617238046Smarcel	id->man_id = fsl_nfc_read_byte(dev);
618238046Smarcel	id->dev_id = fsl_nfc_read_byte(dev);
619238046Smarcel
620238046Smarcel	nand_debug(NDBG_DRV, "manufacturer id: %x chip id: %x",
621238046Smarcel	    id->man_id, id->dev_id);
622238046Smarcel
623238046Smarcel	return (0);
624238046Smarcel}
625238046Smarcel
626238046Smarcel#ifdef NAND_DEBUG_TIMING
627238046Smarcel
628238046Smarcelstatic SYSCTL_NODE(_debug, OID_AUTO, fcm, CTLFLAG_RD, 0, "FCM timing");
629238046Smarcel
630238046Smarcelstatic u_int csct = 1;	/* 22:    Chip select to command time (trlx). */
631238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, csct, CTLFLAG_RW, &csct, 1,
632238046Smarcel    "Chip select to command time: determines how far in advance -LCSn is "
633238046Smarcel    "asserted prior to any bus activity during a NAND Flash access handled "
634238046Smarcel    "by the FCM. This helps meet chip-select setup times for slow memories.");
635238046Smarcel
636238046Smarcelstatic u_int cst = 1;	/* 23:    Command setup time (trlx). */
637238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, cst, CTLFLAG_RW, &cst, 1,
638238046Smarcel    "Command setup time: determines the delay of -LFWE assertion relative to "
639238046Smarcel    "the command, address, or data change when the external memory access "
640238046Smarcel    "is handled by the FCM.");
641238046Smarcel
642238046Smarcelstatic u_int cht = 1;	/* 24:    Command hold time (trlx). */
643238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, cht, CTLFLAG_RW, &cht, 1,
644238046Smarcel    "Command hold time: determines the -LFWE negation prior to the command, "
645238046Smarcel    "address, or data change when the external memory access is handled by "
646238046Smarcel    "the FCM.");
647238046Smarcel
648238046Smarcelstatic u_int scy = 2;	/* 25-27: Cycle length in bus clocks */
649238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, scy, CTLFLAG_RW, &scy, 2,
650238046Smarcel    "Cycle length in bus clocks: see RM");
651238046Smarcel
652238046Smarcelstatic u_int rst = 1;	/* 28:    Read setup time (trlx). */
653238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, rst, CTLFLAG_RW, &rst, 1,
654238046Smarcel    "Read setup time: determines the delay of -LFRE assertion relative to "
655238046Smarcel    "sampling of read data when the external memory access is handled by "
656238046Smarcel    "the FCM.");
657238046Smarcel
658238046Smarcelstatic u_int trlx = 1;	/* 29:    Timing relaxed. */
659238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, trlx, CTLFLAG_RW, &trlx, 1,
660238046Smarcel    "Timing relaxed: modifies the settings of timing parameters for slow "
661238046Smarcel    "memories. See RM");
662238046Smarcel
663238046Smarcelstatic u_int ehtr = 1;	/* 30:    Extended hold time on read accesses. */
664238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, ehtr, CTLFLAG_RW, &ehtr, 1,
665238046Smarcel    "Extended hold time on read accesses: indicates with TRLX how many "
666238046Smarcel    "cycles are inserted between a read access from the current bank and "
667238046Smarcel    "the next access.");
668238046Smarcel
669238046Smarcelstatic u_int
670238046Smarcelfsl_nand_get_timing(void)
671238046Smarcel{
672238046Smarcel	u_int timing;
673238046Smarcel
674238046Smarcel	timing = ((csct & 1) << 9) | ((cst & 1) << 8) | ((cht & 1) << 7) |
675238046Smarcel	    ((scy & 7) << 4) | ((rst & 1) << 3) | ((trlx & 1) << 2) |
676238046Smarcel	    ((ehtr & 1) << 1);
677238046Smarcel
678238046Smarcel	printf("nfc_fsl: timing = %u\n", timing);
679238046Smarcel	return (timing);
680238046Smarcel}
681238046Smarcel
682238046Smarcelstatic int
683238046Smarcelfsl_sysctl_program(SYSCTL_HANDLER_ARGS)
684238046Smarcel{
685238046Smarcel	struct fsl_nand_softc *sc;
686238046Smarcel	int error, i;
687238046Smarcel	device_t dev;
688238046Smarcel	uint32_t or_v;
689238046Smarcel
690238046Smarcel	error = sysctl_wire_old_buffer(req, sizeof(int));
691238046Smarcel	if (error == 0) {
692238046Smarcel		i = 0;
693238046Smarcel		error = sysctl_handle_int(oidp, &i, 0, req);
694238046Smarcel	}
695238046Smarcel	if (error != 0 || req->newptr == NULL)
696238046Smarcel		return (error);
697238046Smarcel
698238046Smarcel	for (i = 0; i < 8; i++) {
699238046Smarcel		dev = fcm_devs[i];
700238046Smarcel		if (dev == NULL)
701238046Smarcel			continue;
702238046Smarcel		sc = device_get_softc(dev);
703238046Smarcel
704238046Smarcel		/* Reprogram OR(x) */
705238046Smarcel		or_v = lbc_read_reg(dev, LBC85XX_OR(sc->dinfo->di_bank));
706238046Smarcel		or_v &= 0xfffffc00;
707238046Smarcel		or_v |= fsl_nand_get_timing();
708238046Smarcel		lbc_write_reg(dev, LBC85XX_OR(sc->dinfo->di_bank), or_v);
709238046Smarcel	}
710238046Smarcel	return (0);
711238046Smarcel}
712238046Smarcel
713238046SmarcelSYSCTL_PROC(_debug_fcm, OID_AUTO, program, CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
714238046Smarcel    fsl_sysctl_program, "I", "write to program FCM with current values");
715238046Smarcel
716238046Smarcel#endif /* NAND_DEBUG_TIMING */
717