svn commit: r233978 - projects/nand/sys/dev/nand

Grzegorz Bernacki gber at FreeBSD.org
Sat Apr 7 04:55:37 UTC 2012


Author: gber
Date: Sat Apr  7 04:55:36 2012
New Revision: 233978
URL: http://svn.freebsd.org/changeset/base/233978

Log:
  Bring back nand_cdev.c

Modified:
  projects/nand/sys/dev/nand/nand_cdev.c

Modified: projects/nand/sys/dev/nand/nand_cdev.c
==============================================================================
--- projects/nand/sys/dev/nand/nand_cdev.c	Sat Apr  7 04:53:32 2012	(r233977)
+++ projects/nand/sys/dev/nand/nand_cdev.c	Sat Apr  7 04:55:36 2012	(r233978)
@@ -0,0 +1,413 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/bio.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include <dev/nand/nand_dev.h>
+#include "nand_if.h"
+#include "nandbus_if.h"
+
+static int nand_page_stat(struct nand_chip *, struct page_stat_io *);
+static int nand_block_stat(struct nand_chip *, struct block_stat_io *);
+
+static d_ioctl_t nand_ioctl;
+static d_open_t nand_open;
+static d_strategy_t nand_strategy;
+
+static struct cdevsw nand_cdevsw = {
+	.d_version	= D_VERSION,
+	.d_name		= "nand",
+	.d_open		= nand_open,
+	.d_read		= physread,
+	.d_write	= physwrite,
+	.d_ioctl	= nand_ioctl,
+	.d_strategy =	nand_strategy,
+};
+
+static int
+offset_to_page(struct chip_geom *cg, uint32_t offset)
+{
+
+	return (offset / cg->page_size);
+}
+
+static int
+offset_to_page_off(struct chip_geom *cg, uint32_t offset)
+{
+
+	return (offset % cg->page_size);
+}
+
+int
+nand_make_dev(struct nand_chip *chip)
+{
+	struct nandbus_ivar *ivar;
+	device_t parent, nandbus;
+	int parent_unit, unit;
+	char *name;
+
+	ivar = device_get_ivars(chip->dev);
+	nandbus = device_get_parent(chip->dev);
+
+	if (ivar->chip_cdev_name) {
+		name = ivar->chip_cdev_name;
+
+		/*
+		 * If we got distinct name for chip device we can enumarete it
+		 * based on contoller number.
+		 */
+		parent = device_get_parent(nandbus);
+	} else {
+		name = "nand";
+		parent = nandbus;
+	}
+
+	parent_unit = device_get_unit(parent);
+	unit = parent_unit * 4 + chip->num;
+	chip->cdev = make_dev(&nand_cdevsw, unit, UID_ROOT, GID_WHEEL,
+	    0666, "%s%d.%d", name, parent_unit, chip->num);
+
+	if (chip->cdev == NULL)
+		return (ENXIO);
+
+	if (bootverbose)
+		device_printf(chip->dev, "Created cdev %s%d.%d for chip "
+		    "[0x%0x, 0x%0x]\n", name, parent_unit, chip->num,
+		    ivar->man_id, ivar->dev_id);
+
+	chip->cdev->si_drv1 = chip;
+
+	return (0);
+}
+
+void
+nand_destroy_dev(struct nand_chip *chip)
+{
+
+	if (chip->cdev)
+		destroy_dev(chip->cdev);
+}
+
+static int
+nand_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+
+	return (0);
+}
+
+static int
+nand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
+{
+	struct chip_geom *cg;
+	device_t nandbus;
+	int start_page, count, off, err = 0;
+	uint8_t *ptr, *tmp;
+
+	nand_debug(NDBG_CDEV, "Read from chip%d [%p] at %d\n", chip->num,
+	    chip, offset);
+
+	nandbus = device_get_parent(chip->dev);
+	NANDBUS_LOCK(nandbus);
+	NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+	cg = &chip->chip_geom;
+	start_page = offset_to_page(cg, offset);
+	off = offset_to_page_off(cg, offset);
+	count = (len > cg->page_size - off) ? cg->page_size - off : len;
+
+	ptr = (uint8_t *)buf;
+	while (len > 0) {
+		if (len < cg->page_size) {
+			tmp = malloc(cg->page_size, M_NAND, M_WAITOK);
+			if (!tmp) {
+				err = ENOMEM;
+				break;
+			}
+			err = NAND_READ_PAGE(chip->dev, start_page,
+			    tmp, cg->page_size, 0);
+			if (err) {
+				free(tmp, M_NAND);
+				break;
+			}
+			bcopy(tmp + off, ptr, count);
+			free(tmp, M_NAND);
+		} else {
+			err = NAND_READ_PAGE(chip->dev, start_page,
+			    ptr, cg->page_size, 0);
+			if (err)
+				break;
+		}
+
+		len -= count;
+		start_page++;
+		ptr += count;
+		count = (len > cg->page_size) ? cg->page_size : len;
+		off = 0;
+	}
+
+	NANDBUS_UNLOCK(nandbus);
+	return (err);
+}
+
+static int
+nand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len)
+{
+	struct chip_geom *cg;
+	device_t nandbus;
+	int off, start_page, err = 0;
+	uint8_t *ptr;
+
+	nand_debug(NDBG_CDEV, "Write to chip %d [%p] at %d\n", chip->num,
+	    chip, offset);
+
+	nandbus = device_get_parent(chip->dev);
+	NANDBUS_LOCK(nandbus);
+	NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+	cg = &chip->chip_geom;
+	start_page = offset_to_page(cg, offset);
+	off = offset_to_page_off(cg, offset);
+
+	if (off != 0 || (len % cg->page_size) != 0) {
+		printf("Not aligned write start [0x%08x] size [0x%08x]\n",
+		    off, len);
+		NANDBUS_UNLOCK(nandbus);
+		return (EINVAL);
+	}
+
+	ptr = (uint8_t *)buf;
+	while (len > 0) {
+		err = NAND_PROGRAM_PAGE(chip->dev, start_page, ptr,
+		    cg->page_size, 0);
+		if (err)
+			break;
+
+		len -= cg->page_size;
+		start_page++;
+		ptr += cg->page_size;
+	}
+
+	NANDBUS_UNLOCK(nandbus);
+	return (err);
+}
+
+static void
+nand_strategy(struct bio *bp)
+{
+	struct nand_chip *chip;
+	struct cdev *dev;
+	int err = 0;
+
+	dev = bp->bio_dev;
+	chip = dev->si_drv1;
+
+	nand_debug(NDBG_CDEV, "Strategy %s on chip %d [%p]\n",
+	    (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" : "WRITE",
+	    chip->num, chip);
+
+	if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
+		err = nand_read(chip,
+		    bp->bio_offset & 0xffffffff,
+		    bp->bio_data, bp->bio_bcount);
+	} else {
+		err = nand_write(chip,
+		    bp->bio_offset & 0xffffffff,
+		    bp->bio_data, bp->bio_bcount);
+	}
+
+	if (err == 0)
+		bp->bio_resid = 0;
+	else {
+		bp->bio_error = EIO;
+		bp->bio_flags |= BIO_ERROR;
+		bp->bio_resid = bp->bio_bcount;
+	}
+
+	biodone(bp);
+}
+
+static int
+nand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset,
+    uint32_t len, uint8_t *data, uint8_t write)
+{
+	struct chip_geom *cg;
+	uint8_t *buf = NULL;
+	int ret = 0;
+
+	cg = &chip->chip_geom;
+
+	buf = malloc(cg->oob_size, M_NAND, M_WAITOK);
+	if (!buf)
+		return (ENOMEM);
+
+	memset(buf, 0xff, cg->oob_size);
+
+	if (!write) {
+		ret = nand_read_oob(chip, page, buf, cg->oob_size);
+		copyout(buf, data, len);
+	} else {
+		copyin(data, buf, len);
+		ret = nand_prog_oob(chip, page, buf, cg->oob_size);
+	}
+
+	free(buf, M_NAND);
+
+	return (ret);
+}
+
+static int
+nand_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+    struct thread *td)
+{
+	struct nand_chip *chip;
+	struct nand_oob_rw *oob_rw = NULL;
+	struct nand_raw_rw *raw_rw = NULL;
+	device_t nandbus;
+	uint8_t *buf = NULL;
+	int ret = 0;
+	uint8_t status;
+
+	chip = (struct nand_chip *)dev->si_drv1;
+	nandbus = device_get_parent(chip->dev);
+
+	if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
+		raw_rw = (struct nand_raw_rw *)data;
+		buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
+	}
+	switch(cmd) {
+	case NAND_IO_ERASE:
+		ret = nand_erase_blocks(chip, ((off_t *)data)[0],
+		    ((off_t *)data)[1]);
+		break;
+
+	case NAND_IO_OOB_READ:
+		oob_rw = (struct nand_oob_rw *)data;
+		ret = nand_oob_access(chip, oob_rw->page, 0,
+		    oob_rw->len, oob_rw->data, 0);
+		break;
+
+	case NAND_IO_OOB_PROG:
+		oob_rw = (struct nand_oob_rw *)data;
+		ret = nand_oob_access(chip, oob_rw->page, 0,
+		    oob_rw->len, oob_rw->data, 1);
+		break;
+
+	case NAND_IO_GET_STATUS:
+		NANDBUS_LOCK(nandbus);
+		ret = NANDBUS_GET_STATUS(nandbus, &status);
+		if (ret == 0)
+			*(uint8_t *)data = status;
+		NANDBUS_UNLOCK(nandbus);
+		break;
+
+	case NAND_IO_RAW_PROG:
+		ret = copyin(raw_rw->data, buf, raw_rw->len);
+		if (ret)
+			break;
+		ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
+		    raw_rw->len);
+		break;
+
+	case NAND_IO_RAW_READ:
+		ret = nand_read_pages_raw(chip, raw_rw->off, buf,
+		    raw_rw->len);
+		if (ret)
+			break;
+		ret = copyout(buf, raw_rw->data, raw_rw->len);
+		break;
+
+	case NAND_IO_PAGE_STAT:
+		ret = nand_page_stat(chip, (struct page_stat_io *)data);
+		break;
+
+	case NAND_IO_BLOCK_STAT:
+		ret = nand_block_stat(chip, (struct block_stat_io *)data);
+		break;
+
+	case NAND_IO_GET_CHIP_PARAM:
+		nand_get_chip_param(chip, (struct chip_param_io *)data);
+		break;
+
+	default:
+		printf("Unknown nand_ioctl request \n");
+		ret = EIO;
+	}
+
+	if (buf)
+		free(buf, M_NAND);
+
+	return (ret);
+}
+
+static int
+nand_page_stat(struct nand_chip *chip, struct page_stat_io *page_stat)
+{
+	struct chip_geom *cg;
+	struct page_stat *stat;
+	int num_pages;
+
+	cg = &chip->chip_geom;
+	num_pages = cg->pgs_per_blk * cg->blks_per_lun * cg->luns;
+	if (page_stat->page_num >= num_pages)
+		return (EINVAL);
+
+	stat = &chip->pg_stat[page_stat->page_num];
+	page_stat->page_read = stat->page_read;
+	page_stat->page_written = stat->page_written;
+	page_stat->page_raw_read = stat->page_raw_read;
+	page_stat->page_raw_written = stat->page_raw_written;
+	page_stat->ecc_succeded = stat->ecc_stat.ecc_succeded;
+	page_stat->ecc_corrected = stat->ecc_stat.ecc_corrected;
+	page_stat->ecc_failed = stat->ecc_stat.ecc_failed;
+
+	return (0);
+}
+
+static int
+nand_block_stat(struct nand_chip *chip, struct block_stat_io *block_stat)
+{
+	struct chip_geom *cg;
+	uint32_t block_num = block_stat->block_num;
+
+	cg = &chip->chip_geom;
+	if (block_num >= cg->blks_per_lun * cg->luns)
+		return (EINVAL);
+
+	block_stat->block_erased = chip->blk_stat[block_num].block_erased;
+
+	return (0);
+}


More information about the svn-src-projects mailing list