/* $NetBSD: omap2_nand.c,v 1.4 2011/07/01 20:30:21 dyoung Exp $ */ /*- * Copyright (c) 2010 Department of Software Engineering, * University of Szeged, Hungary * Copyright (c) 2010 Adam Hoka * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by the Department of Software Engineering, University of Szeged, Hungary * * 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 ``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 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. */ /* Device driver for the NAND controller found in Marvell ARM SoCs. */ #include __KERNEL_RCSID(0, "$NetBSD: omap2_nand.c,v 1.4 2011/07/01 20:30:21 dyoung Exp $"); //#include "opt_omap.h" #include "opt_flash.h" #include #include #include #include #include #include #include #include static int marvell_nand_match(struct device *, struct cfdata *, void *); static void marvell_nand_attach(struct device *, struct device *, void *); static int marvell_nand_detach(device_t, int); void marvell_nand_command(device_t self, uint8_t command); void marvell_nand_address(device_t self, uint8_t address); void marvell_nand_busy(device_t self); void marvell_nand_read_1(device_t self, uint8_t *data); void marvell_nand_write_1(device_t self, uint8_t data); void marvell_nand_read_2(device_t self, uint16_t *data); void marvell_nand_write_2(device_t self, uint16_t data); bool marvell_nand_isbusy(device_t self); void marvell_nand_read_buf_1(device_t self, void *buf, size_t len); void marvell_nand_read_buf_2(device_t self, void *buf, size_t len); void marvell_nand_write_buf_1(device_t self, const void *buf, size_t len); void marvell_nand_write_buf_2(device_t self, const void *buf, size_t len); struct marvell_nand_softc { device_t sc_dev; device_t sc_nanddev; // struct gpmc_softc *sc_gpmcsc; struct nand_interface sc_nand_if; bus_space_handle_t sc_ioh; bus_space_tag_t sc_iot; bus_size_t sc_cmd_reg; bus_size_t sc_addr_reg; bus_size_t sc_data_reg; }; CFATTACH_DECL_NEW(mvnand, sizeof(struct marvell_nand_softc), marvell_nand_match, marvell_nand_attach, marvell_nand_detach, NULL); void marvell_nand_command(device_t self, uint8_t command) { struct marvell_nand_softc *sc = device_private(self); bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_cmd_reg, command); }; void marvell_nand_address(device_t self, uint8_t address) { struct marvell_nand_softc *sc = device_private(self); bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_addr_reg, address); }; bool marvell_nand_isbusy(device_t self) { struct marvell_nand_softc *sc = device_private(self); uint8_t status; DELAY(1); /* just to be sure we are not early */ bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_cmd_reg, ONFI_READ_STATUS); DELAY(1); status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg); return !(status & ONFI_STATUS_RDY); }; static int marvell_nand_match(struct device *parent, struct cfdata *match, void *aux) { // struct marvell_attach_args * const mva = aux; // bus_space_tag_t iot; // bus_space_handle_t ioh; // iot = mva->mva_iot; /* PROFIT??? */ return 1; } static void marvell_nand_attach(device_t parent, device_t self, void *aux) { struct marvell_nand_softc *sc = device_private(self); struct marvell_attach_args * const mva = aux; bus_size_t cs_offset; uint32_t val; sc->sc_dev = self; sc->sc_iot = mva->mva_iot; aprint_normal(": Marvell NAND Controller\n"); aprint_naive("\n"); if (bus_space_subregion(mva->mva_iot, mva->mva_ioh, mva->mva_offset, mva->mva_size, &sc->sc_ioh)) { aprint_error_dev(self, "failed to subregion register space\n"); return; } /* XXX verify this */ sc->sc_cmd_reg = (1 << 0); sc->sc_addr_reg = (1 << 1); sc->sc_data_reg = 0; /* * do the reset dance for NAND */ bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_cmd_reg, ONFI_RESET); marvell_nand_busy(self); nand_init_interface(&sc->sc_nand_if); sc->sc_nand_if.command = &marvell_nand_command; sc->sc_nand_if.address = &marvell_nand_address; sc->sc_nand_if.read_buf_1 = &marvell_nand_read_buf_1; sc->sc_nand_if.read_buf_2 = &marvell_nand_read_buf_2; sc->sc_nand_if.read_1 = &marvell_nand_read_1; sc->sc_nand_if.read_2 = &marvell_nand_read_2; sc->sc_nand_if.write_buf_1 = &marvell_nand_write_buf_1; sc->sc_nand_if.write_buf_2 = &marvell_nand_write_buf_2; sc->sc_nand_if.write_1 = &marvell_nand_write_1; sc->sc_nand_if.write_2 = &marvell_nand_write_2; sc->sc_nand_if.busy = &marvell_nand_busy; sc->sc_nand_if.ecc.necc_code_size = 3; sc->sc_nand_if.ecc.necc_block_size = 256; if (!pmf_device_register1(sc->sc_dev, NULL, NULL, NULL)) aprint_error_dev(sc->sc_dev, "couldn't establish power handler\n"); sc->sc_nanddev = nand_attach_mi(&sc->sc_nand_if, sc->sc_dev); } static int marvell_nand_detach(device_t device, int flags) { struct marvell_nand_softc *sc = device_private(device); int ret = 0; bus_space_unmap(sc->sc_iot, sc->sc_ioh, GPMC_CS_SIZE); pmf_device_deregister(sc->sc_dev); if (sc->sc_nanddev != NULL) ret = config_detach(sc->sc_nanddev, flags); return ret; } void marvell_nand_busy(device_t self) { struct marvell_nand_softc *sc = device_private(self); // while (!(gpmc_register_read(sc->sc_gpmcsc, GPMC_STATUS) & WAIT0)) { // DELAY(1); // } } void marvell_nand_read_1(device_t self, uint8_t *data) { struct marvell_nand_softc *sc = device_private(self); *data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg); } void marvell_nand_write_1(device_t self, uint8_t data) { struct marvell_nand_softc *sc = device_private(self); bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data); } void marvell_nand_read_2(device_t self, uint16_t *data) { struct marvell_nand_softc *sc = device_private(self); *data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg); } void marvell_nand_write_2(device_t self, uint16_t data) { struct marvell_nand_softc *sc = device_private(self); bus_space_write_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data); } void marvell_nand_read_buf_1(device_t self, void *buf, size_t len) { struct marvell_nand_softc *sc = device_private(self); KASSERT(buf != NULL); KASSERT(len >= 1); bus_space_read_multi_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, buf, len); } void marvell_nand_read_buf_2(device_t self, void *buf, size_t len) { struct marvell_nand_softc *sc = device_private(self); KASSERT(buf != NULL); KASSERT(len >= 2); KASSERT(!(len & 0x01)); bus_space_read_multi_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, buf, len / 2); } void marvell_nand_write_buf_1(device_t self, const void *buf, size_t len) { struct marvell_nand_softc *sc = device_private(self); KASSERT(buf != NULL); KASSERT(len >= 1); bus_space_write_multi_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, buf, len); } void marvell_nand_write_buf_2(device_t self, const void *buf, size_t len) { struct marvell_nand_softc *sc = device_private(self); KASSERT(buf != NULL); KASSERT(len >= 2); KASSERT(!(len & 0x01)); bus_space_write_multi_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, buf, len / 2); }