From owner-svn-src-projects@FreeBSD.ORG Sat Apr 7 04:55:37 2012 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id E9B05106564A; Sat, 7 Apr 2012 04:55:36 +0000 (UTC) (envelope-from gber@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id C9BD48FC12; Sat, 7 Apr 2012 04:55:36 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q374ta5p043239; Sat, 7 Apr 2012 04:55:36 GMT (envelope-from gber@svn.freebsd.org) Received: (from gber@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q374taYC043237; Sat, 7 Apr 2012 04:55:36 GMT (envelope-from gber@svn.freebsd.org) Message-Id: <201204070455.q374taYC043237@svn.freebsd.org> From: Grzegorz Bernacki Date: Sat, 7 Apr 2012 04:55:36 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r233978 - projects/nand/sys/dev/nand X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 07 Apr 2012 04:55:37 -0000 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#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); +}