Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 3 Jul 2012 01:00:29 +0000 (UTC)
From:      Marcel Moolenaar <marcel@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r238046 - in head/sys: conf dev/nand
Message-ID:  <201207030100.q6310TIG010148@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: marcel
Date: Tue Jul  3 01:00:29 2012
New Revision: 238046
URL: http://svn.freebsd.org/changeset/base/238046

Log:
  Add a driver for the Freescale FCM module in the localbus controller.
  This driver does not yet handle multiple chip selects properly.
  
  Note that the NAND infrastructure does not perform full page
  reads or writes, which means that this driver cannot make use
  of the hardware ECC that is otherwise present.

Added:
  head/sys/dev/nand/nfc_fsl.c   (contents, props changed)
  head/sys/dev/nand/nfc_fsl.h   (contents, props changed)
Modified:
  head/sys/conf/files.powerpc

Modified: head/sys/conf/files.powerpc
==============================================================================
--- head/sys/conf/files.powerpc	Tue Jul  3 00:06:14 2012	(r238045)
+++ head/sys/conf/files.powerpc	Tue Jul  3 01:00:29 2012	(r238046)
@@ -35,6 +35,7 @@ dev/iicbus/ad7417.c		optional	ad7417 pow
 dev/iicbus/ds1775.c		optional	ds1775 powermac
 dev/iicbus/max6690.c		optional	max6690 powermac
 dev/kbd/kbd.c			optional	sc
+dev/nand/nfc_fsl.c		optional	nand mpc85xx
 dev/ofw/openfirm.c		optional	aim | fdt
 dev/ofw/openfirmio.c		optional	aim | fdt
 dev/ofw/ofw_bus_if.m		optional	aim | fdt

Added: head/sys/dev/nand/nfc_fsl.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/nand/nfc_fsl.c	Tue Jul  3 01:00:29 2012	(r238046)
@@ -0,0 +1,716 @@
+/*-
+ * Copyright (C) 2012 Juniper Networks, Inc.
+ * Copyright (C) 2009-2012 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * TODO :
+ *
+ *  -- test support for small pages
+ *  -- support for reading ONFI parameters
+ *  -- support for cached and interleaving commands
+ *  -- proper setting of AL bits in FMR
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <powerpc/mpc85xx/lbc.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+
+#include "nfc_fsl.h"
+
+#include "nfc_if.h"
+
+#define LBC_READ(regname)	lbc_read_reg(dev, (LBC85XX_ ## regname))
+#define LBC_WRITE(regname, val)	lbc_write_reg(dev, (LBC85XX_ ## regname), val)
+
+enum addr_type {
+	ADDR_NONE,
+	ADDR_ID,
+	ADDR_ROW,
+	ADDR_ROWCOL
+};
+
+struct fsl_nfc_fcm {
+	/* Read-only after initialization */
+	uint32_t	reg_fmr;
+
+	/* To be preserved across "start_command" */
+	u_int		buf_ofs;
+	u_int		read_ptr;
+	u_int		status:1;
+
+	/* Command state -- cleared by "start_command" */
+	uint32_t	fcm_startzero;
+	uint32_t	reg_fcr;
+	uint32_t	reg_fir;
+	uint32_t	reg_mdr;
+	uint32_t	reg_fbcr;
+	uint32_t	reg_fbar;
+	uint32_t	reg_fpar;
+	u_int		cmdnr;
+	u_int		opnr;
+	u_int		pg_ofs;
+	enum addr_type	addr_type;
+	u_int		addr_bytes;
+	u_int		row_addr;
+	u_int		column_addr;
+	u_int		data_fir:8;
+	uint32_t	fcm_endzero;
+};
+
+struct fsl_nand_softc {
+	struct nand_softc		nand_dev;
+	device_t			dev;
+	struct resource			*res;
+	int				rid;		/* Resourceid */
+	struct lbc_devinfo		*dinfo;
+	struct fsl_nfc_fcm		fcm;
+	uint8_t				col_cycles;
+	uint8_t				row_cycles;
+	uint16_t			pgsz;		/* Page size */
+};
+
+static int	fsl_nand_attach(device_t dev);
+static int	fsl_nand_probe(device_t dev);
+static int	fsl_nand_detach(device_t dev);
+
+static int	fsl_nfc_select_cs(device_t dev, uint8_t cs);
+static int	fsl_nfc_read_rnb(device_t dev);
+static int	fsl_nfc_send_command(device_t dev, uint8_t command);
+static int	fsl_nfc_send_address(device_t dev, uint8_t address);
+static uint8_t	fsl_nfc_read_byte(device_t dev);
+static int	fsl_nfc_start_command(device_t dev);
+static void	fsl_nfc_read_buf(device_t dev, void *buf, uint32_t len);
+static void	fsl_nfc_write_buf(device_t dev, void *buf, uint32_t len);
+
+static device_method_t fsl_nand_methods[] = {
+	DEVMETHOD(device_probe,		fsl_nand_probe),
+	DEVMETHOD(device_attach,	fsl_nand_attach),
+	DEVMETHOD(device_detach,	fsl_nand_detach),
+
+	DEVMETHOD(nfc_select_cs,	fsl_nfc_select_cs),
+	DEVMETHOD(nfc_read_rnb,		fsl_nfc_read_rnb),
+	DEVMETHOD(nfc_start_command,	fsl_nfc_start_command),
+	DEVMETHOD(nfc_send_command,	fsl_nfc_send_command),
+	DEVMETHOD(nfc_send_address,	fsl_nfc_send_address),
+	DEVMETHOD(nfc_read_byte,	fsl_nfc_read_byte),
+	DEVMETHOD(nfc_read_buf,		fsl_nfc_read_buf),
+	DEVMETHOD(nfc_write_buf,	fsl_nfc_write_buf),
+	{ 0, 0 },
+};
+
+static driver_t fsl_nand_driver = {
+	"nand",
+	fsl_nand_methods,
+	sizeof(struct fsl_nand_softc),
+};
+
+static devclass_t fsl_nand_devclass;
+
+DRIVER_MODULE(fsl_nand, lbc, fsl_nand_driver, fsl_nand_devclass,
+    0, 0);
+
+static int fsl_nand_build_address(device_t dev, uint32_t page, uint32_t column);
+static int fsl_nand_chip_preprobe(device_t dev, struct nand_id *id);
+
+#ifdef NAND_DEBUG_TIMING
+static device_t fcm_devs[8];
+#endif
+
+#define CMD_SHIFT(cmd_num)	(24 - ((cmd_num) * 8))
+#define OP_SHIFT(op_num)	(28 - ((op_num) * 4))
+
+#define FSL_LARGE_PAGE_SIZE	(2112)
+#define FSL_SMALL_PAGE_SIZE	(528)
+
+static void
+fsl_nand_init_regs(struct fsl_nand_softc *sc)
+{
+	uint32_t or_v, br_v;
+	device_t dev;
+
+	dev = sc->dev;
+
+	sc->fcm.reg_fmr = (15 << FMR_CWTO_SHIFT);
+
+	/*
+	 * Setup 4 row cycles and hope that chip ignores superfluous address
+	 * bytes.
+	 */
+	sc->fcm.reg_fmr |= (2 << FMR_AL_SHIFT);
+
+	/* Reprogram BR(x) */
+	br_v = lbc_read_reg(dev, LBC85XX_BR(sc->dinfo->di_bank));
+	br_v &= 0xffff8000;
+	br_v |= 1 << 11;	/* 8-bit port size */
+	br_v |= 0 << 9;		/* No ECC checking and generation */
+	br_v |= 1 << 5;		/* FCM machine */
+	br_v |= 1;		/* Valid */
+	lbc_write_reg(dev, LBC85XX_BR(sc->dinfo->di_bank), br_v);
+
+	/* Reprogram OR(x) */
+	or_v = lbc_read_reg(dev, LBC85XX_OR(sc->dinfo->di_bank));
+	or_v &= 0xfffffc00;
+	or_v |= 0x03AE;		/* Default POR timing */
+	lbc_write_reg(dev, LBC85XX_OR(sc->dinfo->di_bank), or_v);
+
+	if (or_v & OR_FCM_PAGESIZE) {
+		sc->pgsz = FSL_LARGE_PAGE_SIZE;
+		sc->col_cycles = 2;
+		nand_debug(NDBG_DRV, "%s: large page NAND device at #%d",
+		    device_get_nameunit(dev), sc->dinfo->di_bank);
+	} else {
+		sc->pgsz = FSL_SMALL_PAGE_SIZE;
+		sc->col_cycles = 1;
+		nand_debug(NDBG_DRV, "%s: small page NAND device at #%d",
+		    device_get_nameunit(dev), sc->dinfo->di_bank);
+	}
+}
+
+static int
+fsl_nand_probe(device_t dev)
+{
+
+	if (!ofw_bus_is_compatible(dev, "fsl,elbc-fcm-nand"))
+		return (ENXIO);
+
+	device_set_desc(dev, "Freescale localbus FCM Controller");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+fsl_nand_attach(device_t dev)
+{
+	struct fsl_nand_softc *sc;
+	struct nand_id id;
+	struct nand_params *param;
+	uint32_t num_pages;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->dinfo = device_get_ivars(dev);
+
+	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
+	    RF_ACTIVE);
+	if (sc->res == NULL) {
+		device_printf(dev, "could not allocate resources!\n");
+		return (ENXIO);
+	}
+
+	bzero(&sc->fcm, sizeof(sc->fcm));
+
+	/* Init register and check if HW ECC turned on */
+	fsl_nand_init_regs(sc);
+
+	/* Chip is probed, so determine number of row address cycles */
+	fsl_nand_chip_preprobe(dev, &id);
+	param = nand_get_params(&id);
+	if (param != NULL) {
+		num_pages = (param->chip_size << 20) / param->page_size;
+		while(num_pages) {
+			sc->row_cycles++;
+			num_pages >>= 8;
+		}
+
+		sc->fcm.reg_fmr &= ~(FMR_AL);
+		sc->fcm.reg_fmr |= (sc->row_cycles - 2) << FMR_AL_SHIFT;
+	}
+
+	nand_init(&sc->nand_dev, dev, NAND_ECC_SOFT, 0, 0, NULL, NULL);
+
+#ifdef NAND_DEBUG_TIMING
+	fcm_devs[sc->dinfo->di_bank] = dev;
+#endif
+
+	return (nandbus_create(dev));
+}
+
+static int
+fsl_nand_detach(device_t dev)
+{
+	struct fsl_nand_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (sc->res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
+
+	return (0);
+}
+
+static int
+fsl_nfc_select_cs(device_t dev, uint8_t cs)
+{
+
+	// device_printf(dev, "%s(cs=%u)\n", __func__, cs);
+	return ((cs > 0) ? EINVAL : 0);
+}
+
+static int
+fsl_nfc_read_rnb(device_t dev)
+{
+
+	// device_printf(dev, "%s()\n", __func__);
+	return (0);
+}
+
+static int
+fsl_nfc_send_command(device_t dev, uint8_t command)
+{
+	struct fsl_nand_softc *sc;
+	struct fsl_nfc_fcm *fcm;
+	uint8_t	fir_op;
+
+	// device_printf(dev, "%s(command=%u)\n", __func__, command);
+
+	sc = device_get_softc(dev);
+	fcm = &sc->fcm;
+
+	if (command == NAND_CMD_PROG_END) {
+		fcm->reg_fir |= (FIR_OP_WB << OP_SHIFT(fcm->opnr));
+		fcm->opnr++;
+	}
+	fcm->reg_fcr |= command << CMD_SHIFT(fcm->cmdnr);
+	fir_op = (fcm->cmdnr == 0) ? FIR_OP_CW0 : FIR_OP_CM(fcm->cmdnr);
+	fcm->cmdnr++;
+
+	fcm->reg_fir |= (fir_op << OP_SHIFT(fcm->opnr));
+	fcm->opnr++;
+
+	switch (command) {
+	case NAND_CMD_READ_ID:
+		fcm->data_fir = FIR_OP_RBW;
+		fcm->addr_type = ADDR_ID;
+		break;
+	case NAND_CMD_SMALLOOB:
+		fcm->pg_ofs += 256;
+		/*FALLTHROUGH*/
+	case NAND_CMD_SMALLB:
+		fcm->pg_ofs += 256;
+		/*FALLTHROUGH*/
+	case NAND_CMD_READ: /* NAND_CMD_SMALLA */
+		fcm->data_fir = FIR_OP_RBW;
+		fcm->addr_type = ADDR_ROWCOL;
+		break;
+	case NAND_CMD_STATUS:
+		fcm->data_fir = FIR_OP_RS;
+		fcm->status = 1;
+		break;
+	case NAND_CMD_ERASE:
+		fcm->addr_type = ADDR_ROW;
+		break;
+	case NAND_CMD_PROG:
+		fcm->addr_type = ADDR_ROWCOL;
+		break;
+	}
+	return (0);
+}
+
+static int
+fsl_nfc_send_address(device_t dev, uint8_t addr)
+{
+	struct fsl_nand_softc *sc;
+	struct fsl_nfc_fcm *fcm;
+	uint32_t addr_bits;
+
+	// device_printf(dev, "%s(address=%u)\n", __func__, addr);
+
+	sc = device_get_softc(dev);
+	fcm = &sc->fcm;
+
+	KASSERT(fcm->addr_type != ADDR_NONE,
+	    ("controller doesn't expect address cycle"));
+
+	addr_bits = addr;
+
+	if (fcm->addr_type == ADDR_ID) {
+		fcm->reg_fir |= (FIR_OP_UA << OP_SHIFT(fcm->opnr));
+		fcm->opnr++;
+
+		fcm->reg_fbcr = 5;
+		fcm->reg_fbar = 0;
+		fcm->reg_fpar = 0;
+		fcm->reg_mdr = addr_bits;
+		fcm->buf_ofs = 0;
+		fcm->read_ptr = 0;
+		return (0);
+	}
+
+	if (fcm->addr_type == ADDR_ROW) {
+		addr_bits <<= fcm->addr_bytes * 8;
+		fcm->row_addr |= addr_bits;
+		fcm->addr_bytes++;
+		if (fcm->addr_bytes < sc->row_cycles)
+			return (0);
+	} else {
+		if (fcm->addr_bytes < sc->col_cycles) {
+			addr_bits <<= fcm->addr_bytes * 8;
+			fcm->column_addr |= addr_bits;
+		} else {
+			addr_bits <<= (fcm->addr_bytes - sc->col_cycles) * 8;
+			fcm->row_addr |= addr_bits;
+		}
+		fcm->addr_bytes++;
+		if (fcm->addr_bytes < (sc->row_cycles + sc->col_cycles))
+			return (0);
+	}
+
+	return (fsl_nand_build_address(dev, fcm->row_addr, fcm->column_addr));
+}
+
+static int
+fsl_nand_build_address(device_t dev, uint32_t row, uint32_t column)
+{
+	struct fsl_nand_softc *sc;
+	struct fsl_nfc_fcm *fcm;
+	uint32_t byte_count = 0;
+	uint32_t block_address = 0;
+	uint32_t page_address = 0;
+
+	sc = device_get_softc(dev);
+	fcm = &sc->fcm;
+
+	fcm->read_ptr = 0;
+	fcm->buf_ofs = 0;
+
+	if (fcm->addr_type == ADDR_ROWCOL) {
+		fcm->reg_fir |= (FIR_OP_CA << OP_SHIFT(fcm->opnr));
+		fcm->opnr++;
+
+		column += fcm->pg_ofs;
+		fcm->pg_ofs = 0;
+
+		page_address |= column;
+
+		if (column != 0) {
+			byte_count = sc->pgsz - column;
+			fcm->read_ptr = column;
+		}
+	}
+
+	fcm->reg_fir |= (FIR_OP_PA << OP_SHIFT(fcm->opnr));
+	fcm->opnr++;
+
+	if (sc->pgsz == FSL_LARGE_PAGE_SIZE) {
+		block_address = row >> 6;
+		page_address |= ((row << FPAR_LP_PI_SHIFT) & FPAR_LP_PI);
+		fcm->buf_ofs = (row & 1) * 4096;
+	} else {
+		block_address = row >> 5;
+		page_address |= ((row << FPAR_SP_PI_SHIFT) & FPAR_SP_PI);
+		fcm->buf_ofs = (row & 7) * 1024;
+	}
+
+	fcm->reg_fbcr = byte_count;
+	fcm->reg_fbar = block_address;
+	fcm->reg_fpar = page_address;
+	return (0);
+}
+
+static int
+fsl_nfc_start_command(device_t dev)
+{
+	struct fsl_nand_softc *sc;
+	struct fsl_nfc_fcm *fcm;
+	uint32_t fmr, ltesr_v;
+	int error, timeout;
+
+	// device_printf(dev, "%s()\n", __func__);
+
+	sc = device_get_softc(dev);
+	fcm = &sc->fcm;
+
+	fmr = fcm->reg_fmr | FMR_OP;
+
+	if (fcm->data_fir)
+		fcm->reg_fir |= (fcm->data_fir << OP_SHIFT(fcm->opnr));
+
+	LBC_WRITE(FIR, fcm->reg_fir);
+	LBC_WRITE(FCR, fcm->reg_fcr);
+
+	LBC_WRITE(FMR, fmr);
+
+	LBC_WRITE(FBCR, fcm->reg_fbcr);
+	LBC_WRITE(FBAR, fcm->reg_fbar);
+	LBC_WRITE(FPAR, fcm->reg_fpar);
+
+	if (fcm->addr_type == ADDR_ID)
+		LBC_WRITE(MDR, fcm->reg_mdr);
+
+	nand_debug(NDBG_DRV, "BEFORE:\nFMR=%#x, FIR=%#x, FCR=%#x", fmr,
+	    fcm->reg_fir, fcm->reg_fcr);
+	nand_debug(NDBG_DRV, "MDR=%#x, FBAR=%#x, FPAR=%#x, FBCR=%#x",
+	    LBC_READ(MDR), fcm->reg_fbar, fcm->reg_fpar, fcm->reg_fbcr);
+
+	LBC_WRITE(LSOR, sc->dinfo->di_bank);
+
+	timeout = (cold) ? FSL_FCM_WAIT_TIMEOUT : ~0;
+	error = 0;
+	ltesr_v = LBC_READ(LTESR);
+	while (!error && (ltesr_v & LTESR_CC) == 0) {
+		if (cold) {
+			DELAY(1000);
+			timeout--;
+			if (timeout < 0)
+				error = EWOULDBLOCK;
+		} else
+			error = tsleep(device_get_parent(sc->dev), PRIBIO,
+			    "nfcfsl", hz);
+		ltesr_v = LBC_READ(LTESR);
+	}
+	if (error)
+		nand_debug(NDBG_DRV, "Command complete wait timeout\n");
+
+	nand_debug(NDBG_DRV, "AFTER:\nLTESR=%#x, LTEDR=%#x, LTEIR=%#x,"
+	    " LTEATR=%#x, LTEAR=%#x, LTECCR=%#x", ltesr_v,
+	    LBC_READ(LTEDR), LBC_READ(LTEIR), LBC_READ(LTEATR),
+	    LBC_READ(LTEAR), LBC_READ(LTECCR));
+
+	bzero(&fcm->fcm_startzero,
+	    __rangeof(struct fsl_nfc_fcm, fcm_startzero, fcm_endzero));
+
+	if (fcm->status)
+		sc->fcm.reg_mdr = LBC_READ(MDR);
+
+	/* Even if timeout occured, we should perform steps below */
+	LBC_WRITE(LTESR, ltesr_v);
+	LBC_WRITE(LTEATR, 0);
+
+	return (error);
+}
+
+static uint8_t
+fsl_nfc_read_byte(device_t dev)
+{
+	struct fsl_nand_softc *sc = device_get_softc(dev);
+	uint32_t offset;
+
+	// device_printf(dev, "%s()\n", __func__);
+
+	/*
+	 * LBC controller allows us to read status into a MDR instead of FCM
+	 * buffer. If last operation requested before read_byte() was STATUS,
+	 * then return MDR instead of reading a single byte from a buffer.
+	 */
+	if (sc->fcm.status) {
+		sc->fcm.status = 0;
+		return (sc->fcm.reg_mdr);
+	}
+
+	KASSERT(sc->fcm.read_ptr < sc->pgsz,
+	    ("Attempt to read beyond buffer %x %x", sc->fcm.read_ptr,
+	    sc->pgsz));
+
+	offset = sc->fcm.buf_ofs + sc->fcm.read_ptr;
+	sc->fcm.read_ptr++;
+	return (bus_read_1(sc->res, offset));
+}
+
+static void
+fsl_nfc_read_buf(device_t dev, void *buf, uint32_t len)
+{
+	struct fsl_nand_softc *sc = device_get_softc(dev);
+	uint32_t offset;
+	int bytesleft = 0;
+
+	// device_printf(dev, "%s(buf=%p, len=%u)\n", __func__, buf, len);
+
+	nand_debug(NDBG_DRV, "REQUEST OF 0x%0x B (BIB=0x%0x, NTR=0x%0x)",
+	    len, sc->pgsz, sc->fcm.read_ptr);
+
+	bytesleft = MIN((unsigned int)len, sc->pgsz - sc->fcm.read_ptr);
+
+	offset = sc->fcm.buf_ofs + sc->fcm.read_ptr;
+	bus_read_region_1(sc->res, offset, buf, bytesleft);
+	sc->fcm.read_ptr += bytesleft;
+}
+
+static void
+fsl_nfc_write_buf(device_t dev, void *buf, uint32_t len)
+{
+	struct fsl_nand_softc *sc = device_get_softc(dev);
+	uint32_t offset;
+	int bytesleft = 0;
+
+	// device_printf(dev, "%s(buf=%p, len=%u)\n", __func__, buf, len);
+
+	KASSERT(len <= sc->pgsz - sc->fcm.read_ptr,
+	    ("Attempt to write beyond buffer"));
+
+	bytesleft = MIN((unsigned int)len, sc->pgsz - sc->fcm.read_ptr);
+
+	nand_debug(NDBG_DRV, "REQUEST TO WRITE 0x%0x (BIB=0x%0x, NTR=0x%0x)",
+	    bytesleft, sc->pgsz, sc->fcm.read_ptr);
+
+	offset = sc->fcm.buf_ofs + sc->fcm.read_ptr;
+	bus_write_region_1(sc->res, offset, buf, bytesleft);
+	sc->fcm.read_ptr += bytesleft;
+}
+
+static int
+fsl_nand_chip_preprobe(device_t dev, struct nand_id *id)
+{
+
+	if (fsl_nfc_send_command(dev, NAND_CMD_RESET) != 0)
+		return (ENXIO);
+
+	if (fsl_nfc_start_command(dev) != 0)
+		return (ENXIO);
+
+	DELAY(1000);
+
+	if (fsl_nfc_send_command(dev, NAND_CMD_READ_ID))
+		return (ENXIO);
+
+	if (fsl_nfc_send_address(dev, 0))
+		return (ENXIO);
+
+	if (fsl_nfc_start_command(dev) != 0)
+		return (ENXIO);
+
+	DELAY(25);
+
+	id->man_id = fsl_nfc_read_byte(dev);
+	id->dev_id = fsl_nfc_read_byte(dev);
+
+	nand_debug(NDBG_DRV, "manufacturer id: %x chip id: %x",
+	    id->man_id, id->dev_id);
+
+	return (0);
+}
+
+#ifdef NAND_DEBUG_TIMING
+
+static SYSCTL_NODE(_debug, OID_AUTO, fcm, CTLFLAG_RD, 0, "FCM timing");
+
+static u_int csct = 1;	/* 22:    Chip select to command time (trlx). */
+SYSCTL_UINT(_debug_fcm, OID_AUTO, csct, CTLFLAG_RW, &csct, 1,
+    "Chip select to command time: determines how far in advance -LCSn is "
+    "asserted prior to any bus activity during a NAND Flash access handled "
+    "by the FCM. This helps meet chip-select setup times for slow memories.");
+
+static u_int cst = 1;	/* 23:    Command setup time (trlx). */
+SYSCTL_UINT(_debug_fcm, OID_AUTO, cst, CTLFLAG_RW, &cst, 1,
+    "Command setup time: determines the delay of -LFWE assertion relative to "
+    "the command, address, or data change when the external memory access "
+    "is handled by the FCM.");
+
+static u_int cht = 1;	/* 24:    Command hold time (trlx). */
+SYSCTL_UINT(_debug_fcm, OID_AUTO, cht, CTLFLAG_RW, &cht, 1,
+    "Command hold time: determines the -LFWE negation prior to the command, "
+    "address, or data change when the external memory access is handled by "
+    "the FCM.");
+
+static u_int scy = 2;	/* 25-27: Cycle length in bus clocks */
+SYSCTL_UINT(_debug_fcm, OID_AUTO, scy, CTLFLAG_RW, &scy, 2,
+    "Cycle length in bus clocks: see RM");
+
+static u_int rst = 1;	/* 28:    Read setup time (trlx). */
+SYSCTL_UINT(_debug_fcm, OID_AUTO, rst, CTLFLAG_RW, &rst, 1,
+    "Read setup time: determines the delay of -LFRE assertion relative to "
+    "sampling of read data when the external memory access is handled by "
+    "the FCM.");
+
+static u_int trlx = 1;	/* 29:    Timing relaxed. */
+SYSCTL_UINT(_debug_fcm, OID_AUTO, trlx, CTLFLAG_RW, &trlx, 1,
+    "Timing relaxed: modifies the settings of timing parameters for slow "
+    "memories. See RM");
+
+static u_int ehtr = 1;	/* 30:    Extended hold time on read accesses. */
+SYSCTL_UINT(_debug_fcm, OID_AUTO, ehtr, CTLFLAG_RW, &ehtr, 1,
+    "Extended hold time on read accesses: indicates with TRLX how many "
+    "cycles are inserted between a read access from the current bank and "
+    "the next access.");
+
+static u_int
+fsl_nand_get_timing(void)
+{
+	u_int timing;
+
+	timing = ((csct & 1) << 9) | ((cst & 1) << 8) | ((cht & 1) << 7) |
+	    ((scy & 7) << 4) | ((rst & 1) << 3) | ((trlx & 1) << 2) |
+	    ((ehtr & 1) << 1);
+
+	printf("nfc_fsl: timing = %u\n", timing);
+	return (timing);
+}
+
+static int
+fsl_sysctl_program(SYSCTL_HANDLER_ARGS)
+{
+	struct fsl_nand_softc *sc;
+	int error, i;
+	device_t dev;
+	uint32_t or_v;
+
+	error = sysctl_wire_old_buffer(req, sizeof(int));
+	if (error == 0) {
+		i = 0;
+		error = sysctl_handle_int(oidp, &i, 0, req);
+	}
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+
+	for (i = 0; i < 8; i++) {
+		dev = fcm_devs[i];
+		if (dev == NULL)
+			continue;
+		sc = device_get_softc(dev);
+
+		/* Reprogram OR(x) */
+		or_v = lbc_read_reg(dev, LBC85XX_OR(sc->dinfo->di_bank));
+		or_v &= 0xfffffc00;
+		or_v |= fsl_nand_get_timing();
+		lbc_write_reg(dev, LBC85XX_OR(sc->dinfo->di_bank), or_v);
+	}
+	return (0);
+}
+
+SYSCTL_PROC(_debug_fcm, OID_AUTO, program, CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
+    fsl_sysctl_program, "I", "write to program FCM with current values");
+
+#endif /* NAND_DEBUG_TIMING */

Added: head/sys/dev/nand/nfc_fsl.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/nand/nfc_fsl.h	Tue Jul  3 01:00:29 2012	(r238046)
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (C) 2012 Juniper Networks, Inc.
+ * Copyright (C) 2009-2012 Semihalf
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NAND_NFC_FSL_H_
+#define	_NAND_NFC_FSL_H_
+
+/* LBC BR/OR Registers layout definitions */
+#define BR_V		0x00000001
+#define BR_V_SHIFT	0
+#define BR_MSEL		0x000000E0
+#define BR_MSEL_SHIFT	5
+#define BR_DECC_CHECK_MODE	0x00000600
+#define BR_DECC_CHECK_GEN	0x00000400
+
+#define OR_FCM_PAGESIZE		0x00000400
+
+/* Options definitions */
+#define NAND_OPT_ECC_MODE_HW	1
+#define NAND_OPT_ECC_MODE_SOFT	(1 << 1)
+
+/* FMR - Flash Mode Register */
+#define FMR_CWTO	0xF000
+#define FMR_CWTO_SHIFT	12
+#define FMR_BOOT	0x0800
+#define FMR_ECCM	0x0100
+#define FMR_AL		0x0030
+#define FMR_AL_SHIFT	4
+#define FMR_OP		0x0003
+#define FMR_OP_SHIFT	0
+
+#define FIR_OP_NOP	0x0 /* No operation and end of sequence */
+#define FIR_OP_CA	0x1 /* Issue current column address */
+#define FIR_OP_PA	0x2 /* Issue current block+page address */
+#define FIR_OP_UA	0x3 /* Issue user defined address */
+#define	FIR_OP_CM(x)	(4 + (x))	/* Issue command from FCR[CMD(x)] */
+#define FIR_OP_WB	0x8 /* Write FBCR bytes from FCM buffer */
+#define FIR_OP_WS	0x9 /* Write 1 or 2 bytes from MDR[AS] */
+#define FIR_OP_RB	0xA /* Read FBCR bytes to FCM buffer */
+#define FIR_OP_RS	0xB /* Read 1 or 2 bytes to MDR[AS] */
+#define FIR_OP_CW0	0xC /* Wait then issue FCR[CMD0] */
+#define FIR_OP_CW1	0xD /* Wait then issue FCR[CMD1] */
+#define FIR_OP_RBW	0xE /* Wait then read FBCR bytes */
+#define FIR_OP_RSW	0xF /* Wait then read 1 or 2 bytes */
+
+/* LTESR - Transfer Error Status Register */
+#define LTESR_BM	0x80000000
+#define LTESR_FCT	0x40000000
+#define LTESR_PAR	0x20000000
+#define LTESR_WP	0x04000000
+#define LTESR_ATMW	0x00800000
+#define LTESR_ATMR	0x00400000
+#define LTESR_CS	0x00080000
+#define LTESR_CC	0x00000001
+
+#define LTESR_NAND_MASK	(LTESR_FCT | LTESR_CC | LTESR_CS)
+
+/* FPAR - Flash Page Address Register */
+#define FPAR_SP_PI		0x00007C00
+#define FPAR_SP_PI_SHIFT	10
+#define FPAR_SP_MS		0x00000200
+#define FPAR_SP_CI		0x000001FF
+#define FPAR_SP_CI_SHIFT	0
+#define FPAR_LP_PI		0x0003F000
+#define FPAR_LP_PI_SHIFT	12
+#define FPAR_LP_MS		0x00000800
+#define FPAR_LP_CI		0x000007FF
+#define FPAR_LP_CI_SHIFT	0
+
+#define FSL_FCM_WAIT_TIMEOUT	10
+
+#endif /* _NAND_NFC_FSL_H_ */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201207030100.q6310TIG010148>