Home | History | Annotate | Line # | Download | only in ti
omap2_nand.c revision 1.3.2.1
      1  1.3.2.1   thorpej /*	$NetBSD: omap2_nand.c,v 1.3.2.1 2021/03/23 07:14:45 thorpej Exp $	*/
      2      1.1  jmcneill 
      3      1.1  jmcneill /*-
      4      1.1  jmcneill  * Copyright (c) 2010 Department of Software Engineering,
      5      1.1  jmcneill  *		      University of Szeged, Hungary
      6      1.1  jmcneill  * Copyright (c) 2010 Adam Hoka <ahoka (at) NetBSD.org>
      7      1.1  jmcneill  * All rights reserved.
      8      1.1  jmcneill  *
      9      1.1  jmcneill  * This code is derived from software contributed to The NetBSD Foundation
     10      1.1  jmcneill  * by the Department of Software Engineering, University of Szeged, Hungary
     11      1.1  jmcneill  *
     12      1.1  jmcneill  * Redistribution and use in source and binary forms, with or without
     13      1.1  jmcneill  * modification, are permitted provided that the following conditions
     14      1.1  jmcneill  * are met:
     15      1.1  jmcneill  * 1. Redistributions of source code must retain the above copyright
     16      1.1  jmcneill  *    notice, this list of conditions and the following disclaimer.
     17      1.1  jmcneill  * 2. Redistributions in binary form must reproduce the above copyright
     18      1.1  jmcneill  *    notice, this list of conditions and the following disclaimer in the
     19      1.1  jmcneill  *    documentation and/or other materials provided with the distribution.
     20      1.1  jmcneill  *
     21      1.1  jmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     22      1.1  jmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23      1.1  jmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     24      1.1  jmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     25      1.1  jmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     26      1.1  jmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     27      1.1  jmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     28      1.1  jmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     29      1.1  jmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30      1.1  jmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31      1.1  jmcneill  * SUCH DAMAGE.
     32      1.1  jmcneill  */
     33      1.1  jmcneill 
     34      1.1  jmcneill /* Device driver for the NAND controller found in Texas Instruments OMAP2
     35      1.1  jmcneill  * and later SOCs.
     36      1.1  jmcneill  */
     37      1.1  jmcneill 
     38      1.1  jmcneill #include <sys/cdefs.h>
     39  1.3.2.1   thorpej __KERNEL_RCSID(0, "$NetBSD: omap2_nand.c,v 1.3.2.1 2021/03/23 07:14:45 thorpej Exp $");
     40      1.1  jmcneill 
     41      1.1  jmcneill /* TODO move to opt_* */
     42      1.1  jmcneill #undef OMAP2_NAND_HARDWARE_ECC
     43      1.1  jmcneill 
     44      1.1  jmcneill #include <sys/param.h>
     45      1.1  jmcneill #include <sys/systm.h>
     46      1.1  jmcneill #include <sys/cdefs.h>
     47      1.1  jmcneill #include <sys/device.h>
     48      1.1  jmcneill 
     49      1.1  jmcneill #include <sys/bus.h>
     50      1.1  jmcneill 
     51      1.1  jmcneill #include <arm/ti/omap2_gpmcreg.h>
     52      1.1  jmcneill 
     53      1.1  jmcneill #include <dev/nand/nand.h>
     54      1.1  jmcneill #include <dev/nand/onfi.h>
     55      1.1  jmcneill 
     56      1.1  jmcneill #include <dev/fdt/fdtvar.h>
     57      1.1  jmcneill 
     58      1.1  jmcneill extern struct flash_interface nand_flash_if;
     59      1.1  jmcneill extern int flash_print(void *, const char *);
     60      1.1  jmcneill 
     61      1.1  jmcneill /* GPMC_STATUS */
     62      1.1  jmcneill #define WAIT0		__BIT(8)	/* active low */
     63      1.1  jmcneill 
     64      1.1  jmcneill /* GPMC_ECC_CONTROL */
     65      1.1  jmcneill #define ECCCLEAR	__BIT(8)
     66      1.1  jmcneill #define ECCPOINTER	__BITS(3,0)
     67      1.1  jmcneill 
     68      1.1  jmcneill /* GPMC_ECC_CONFIG */
     69      1.1  jmcneill #define ECCALGORITHM	__BIT(16)
     70      1.1  jmcneill #define ECCCS		__BITS(3,1)
     71      1.1  jmcneill #define ECC16B		__BIT(7)
     72      1.1  jmcneill #define ECCENABLE	__BIT(0)
     73      1.1  jmcneill /* GPMC_ECC_SIZE_CONFIG */
     74      1.1  jmcneill #define ECCSIZE1	__BITS(29,22)
     75      1.1  jmcneill 
     76      1.1  jmcneill /* GPMC_CONFIG1_i */
     77      1.1  jmcneill #define DEVICETYPE	__BITS(11,10)
     78      1.1  jmcneill #define DEVICESIZE	__BITS(13,12)
     79      1.1  jmcneill 
     80      1.1  jmcneill #define MASKEDINT(mask, integer) ((integer) << (ffs(mask) - 1) & mask)
     81      1.1  jmcneill 
     82      1.1  jmcneill /* NAND status register */
     83      1.1  jmcneill #define NAND_WP_BIT __BIT(4)
     84      1.1  jmcneill 
     85      1.1  jmcneill static int	omap2_nand_match(device_t, cfdata_t, void *);
     86      1.1  jmcneill static void	omap2_nand_attach(device_t, device_t, void *);
     87      1.1  jmcneill 
     88      1.1  jmcneill static void omap2_nand_command(device_t self, uint8_t command);
     89      1.1  jmcneill static void omap2_nand_address(device_t self, uint8_t address);
     90      1.1  jmcneill static void omap2_nand_busy(device_t self);
     91      1.1  jmcneill static void omap2_nand_read_1(device_t self, uint8_t *data);
     92      1.1  jmcneill static void omap2_nand_write_1(device_t self, uint8_t data);
     93      1.1  jmcneill static void omap2_nand_read_2(device_t self, uint16_t *data);
     94      1.1  jmcneill static void omap2_nand_write_2(device_t self, uint16_t data);
     95      1.1  jmcneill bool omap2_nand_isbusy(device_t self);
     96      1.1  jmcneill static void omap2_nand_read_buf_1(device_t self, void *buf, size_t len);
     97      1.1  jmcneill static void omap2_nand_read_buf_2(device_t self, void *buf, size_t len);
     98      1.1  jmcneill static void omap2_nand_write_buf_1(device_t self, const void *buf, size_t len);
     99      1.1  jmcneill static void omap2_nand_write_buf_2(device_t self, const void *buf, size_t len);
    100      1.1  jmcneill 
    101      1.1  jmcneill #ifdef OMAP2_NAND_HARDWARE_ECC
    102      1.1  jmcneill static int omap2_nand_ecc_init(device_t self);
    103      1.1  jmcneill static int omap2_nand_ecc_prepare(device_t self, int mode);
    104      1.1  jmcneill static int omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc);
    105      1.1  jmcneill static int omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc,
    106      1.1  jmcneill     const uint8_t *calcecc);
    107      1.1  jmcneill #endif
    108      1.1  jmcneill 
    109      1.1  jmcneill struct omap2_nand_softc {
    110      1.1  jmcneill 	device_t sc_dev;
    111      1.1  jmcneill 	device_t sc_nanddev;
    112      1.1  jmcneill 
    113      1.1  jmcneill 	int sc_cs;
    114      1.1  jmcneill 	int sc_buswidth;	/* 0: 8bit, 1: 16bit */
    115      1.1  jmcneill 
    116      1.1  jmcneill 	struct nand_interface	sc_nand_if;
    117      1.1  jmcneill 
    118      1.1  jmcneill 	bus_space_tag_t		sc_iot;
    119      1.1  jmcneill 	bus_space_handle_t	sc_ioh;
    120      1.1  jmcneill 	bus_space_handle_t	sc_gpmc_ioh;
    121      1.1  jmcneill 
    122      1.1  jmcneill 	bus_size_t		sc_cmd_reg;
    123      1.1  jmcneill 	bus_size_t		sc_addr_reg;
    124      1.1  jmcneill 	bus_size_t		sc_data_reg;
    125      1.1  jmcneill };
    126      1.1  jmcneill 
    127      1.3   thorpej static const struct device_compatible_entry compat_data[] = {
    128      1.3   thorpej 	{ .compat = "ti,omap2-nand" },
    129      1.3   thorpej 	{ .compat = "ti,omap2-onenand" },
    130      1.3   thorpej 	DEVICE_COMPAT_EOL
    131      1.1  jmcneill };
    132      1.1  jmcneill 
    133      1.1  jmcneill CFATTACH_DECL_NEW(omapnand, sizeof(struct omap2_nand_softc), omap2_nand_match,
    134      1.1  jmcneill     omap2_nand_attach, NULL, NULL);
    135      1.1  jmcneill 
    136      1.1  jmcneill static inline uint32_t
    137      1.1  jmcneill gpmc_register_read(struct omap2_nand_softc *sc, bus_size_t reg)
    138      1.1  jmcneill {
    139      1.1  jmcneill 	return bus_space_read_4(sc->sc_iot, sc->sc_gpmc_ioh, reg);
    140      1.1  jmcneill }
    141      1.1  jmcneill 
    142      1.1  jmcneill static inline void
    143      1.1  jmcneill gpmc_register_write(struct omap2_nand_softc *sc, bus_size_t reg, const uint32_t data)
    144      1.1  jmcneill {
    145      1.1  jmcneill 	bus_space_write_4(sc->sc_iot, sc->sc_gpmc_ioh, reg, data);
    146      1.1  jmcneill }
    147      1.1  jmcneill 
    148      1.1  jmcneill static void
    149      1.1  jmcneill omap2_nand_command(device_t self, uint8_t command)
    150      1.1  jmcneill {
    151      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    152      1.1  jmcneill 
    153      1.1  jmcneill 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_cmd_reg, command);
    154      1.1  jmcneill };
    155      1.1  jmcneill 
    156      1.1  jmcneill static void
    157      1.1  jmcneill omap2_nand_address(device_t self, uint8_t address)
    158      1.1  jmcneill {
    159      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    160      1.1  jmcneill 
    161      1.1  jmcneill 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_addr_reg, address);
    162      1.1  jmcneill };
    163      1.1  jmcneill 
    164      1.1  jmcneill bool
    165      1.1  jmcneill omap2_nand_isbusy(device_t self)
    166      1.1  jmcneill {
    167      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    168      1.1  jmcneill 	uint8_t status;
    169      1.1  jmcneill 
    170      1.1  jmcneill 	DELAY(1);		/* just to be sure we are not early */
    171      1.1  jmcneill 
    172      1.1  jmcneill 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
    173      1.1  jmcneill 	    sc->sc_cmd_reg, ONFI_READ_STATUS);
    174      1.1  jmcneill 
    175      1.1  jmcneill 	DELAY(1);
    176      1.1  jmcneill 
    177      1.1  jmcneill 	status = bus_space_read_1(sc->sc_iot,
    178      1.1  jmcneill 	    sc->sc_ioh, sc->sc_data_reg);
    179      1.1  jmcneill 
    180      1.1  jmcneill 	return !(status & ONFI_STATUS_RDY);
    181      1.1  jmcneill };
    182      1.1  jmcneill 
    183      1.1  jmcneill static int
    184      1.1  jmcneill omap2_nand_match(device_t parent, cfdata_t match, void *aux)
    185      1.1  jmcneill {
    186      1.1  jmcneill 	struct fdt_attach_args * const faa = aux;
    187      1.1  jmcneill 
    188      1.3   thorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
    189      1.1  jmcneill }
    190      1.1  jmcneill 
    191      1.1  jmcneill static void
    192      1.1  jmcneill omap2_nand_attach(device_t parent, device_t self, void *aux)
    193      1.1  jmcneill {
    194      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    195      1.1  jmcneill 	struct fdt_attach_args * const faa = aux;
    196      1.1  jmcneill 	const int phandle = faa->faa_phandle;
    197      1.1  jmcneill 	struct flash_attach_args flash;
    198      1.1  jmcneill 	bus_addr_t addr, part_addr;
    199      1.1  jmcneill 	bus_size_t size, part_size;
    200      1.1  jmcneill 	const u_int *prop;
    201      1.1  jmcneill 	uint32_t val;
    202      1.1  jmcneill 	int len, child;
    203      1.1  jmcneill 
    204      1.1  jmcneill 	if (fdtbus_get_reg(OF_parent(phandle), 0, &addr, &size) != 0) {
    205      1.1  jmcneill 		aprint_error(": couldn't get registers\n");
    206      1.1  jmcneill 		return;
    207      1.1  jmcneill 	}
    208      1.1  jmcneill 
    209      1.1  jmcneill 	sc->sc_iot = faa->faa_bst;
    210      1.1  jmcneill 	sc->sc_dev = self;
    211      1.1  jmcneill 
    212      1.1  jmcneill 	prop = fdtbus_get_prop(phandle, "reg", &len);
    213      1.1  jmcneill 	if (prop == NULL || len < 4) {
    214      1.1  jmcneill 		aprint_error(": couldn't read reg property\n");
    215      1.1  jmcneill 		return;
    216      1.1  jmcneill 	}
    217      1.1  jmcneill 
    218      1.1  jmcneill 	sc->sc_cs = be32toh(prop[0]);
    219      1.1  jmcneill 
    220      1.1  jmcneill 	/* map i/o space */
    221      1.1  jmcneill 	if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_gpmc_ioh) != 0) {
    222      1.1  jmcneill 		aprint_error(": couldn't map registers\n");
    223      1.1  jmcneill 		return;
    224      1.1  jmcneill 	}
    225      1.1  jmcneill 	if (bus_space_subregion(sc->sc_iot, sc->sc_gpmc_ioh, GPMC_CS_CONFIG(sc->sc_cs), 0x30, &sc->sc_ioh) != 0) {
    226      1.1  jmcneill 		aprint_error(": couldn't map cs registers\n");
    227      1.1  jmcneill 		return;
    228      1.1  jmcneill 	}
    229      1.1  jmcneill 
    230      1.1  jmcneill 	aprint_naive("\n");
    231      1.1  jmcneill 	aprint_normal(": CS%d\n", sc->sc_cs);
    232      1.1  jmcneill 
    233      1.1  jmcneill         sc->sc_cmd_reg = GPMC_NAND_COMMAND_0 - GPMC_CONFIG1_0;
    234      1.1  jmcneill 	sc->sc_addr_reg = GPMC_NAND_ADDRESS_0 - GPMC_CONFIG1_0;
    235      1.1  jmcneill 	sc->sc_data_reg = GPMC_NAND_DATA_0 - GPMC_CONFIG1_0;
    236      1.1  jmcneill 
    237      1.1  jmcneill 	/* turn off write protection if enabled */
    238      1.1  jmcneill 	val = gpmc_register_read(sc, GPMC_CONFIG);
    239      1.1  jmcneill 	val |= NAND_WP_BIT;
    240      1.1  jmcneill 	gpmc_register_write(sc, GPMC_CONFIG, val);
    241      1.1  jmcneill 
    242      1.1  jmcneill 	/*
    243      1.1  jmcneill 	 * do the reset dance for NAND
    244      1.1  jmcneill 	 */
    245      1.1  jmcneill 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
    246      1.1  jmcneill 	    sc->sc_cmd_reg, ONFI_RESET);
    247      1.1  jmcneill 
    248      1.1  jmcneill 	omap2_nand_busy(self);
    249      1.1  jmcneill 
    250      1.1  jmcneill 	/* read GPMC_CONFIG1_i to get buswidth */
    251      1.1  jmcneill 	val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPMC_CONFIG1_i);
    252      1.1  jmcneill 
    253      1.1  jmcneill 	if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x01)) {
    254      1.1  jmcneill 		/* 16bit */
    255      1.1  jmcneill 		sc->sc_buswidth = 1;
    256      1.1  jmcneill 	} else if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x00)) {
    257      1.1  jmcneill 		/* 8bit */
    258      1.1  jmcneill 		sc->sc_buswidth = 0;
    259      1.1  jmcneill 	} else {
    260      1.1  jmcneill 		panic("invalid buswidth reported by config1");
    261      1.1  jmcneill 	}
    262      1.1  jmcneill 
    263      1.1  jmcneill 	nand_init_interface(&sc->sc_nand_if);
    264      1.1  jmcneill 
    265      1.1  jmcneill 	sc->sc_nand_if.command = &omap2_nand_command;
    266      1.1  jmcneill 	sc->sc_nand_if.address = &omap2_nand_address;
    267      1.1  jmcneill 	sc->sc_nand_if.read_buf_1 = &omap2_nand_read_buf_1;
    268      1.1  jmcneill 	sc->sc_nand_if.read_buf_2 = &omap2_nand_read_buf_2;
    269      1.1  jmcneill 	sc->sc_nand_if.read_1 = &omap2_nand_read_1;
    270      1.1  jmcneill 	sc->sc_nand_if.read_2 = &omap2_nand_read_2;
    271      1.1  jmcneill 	sc->sc_nand_if.write_buf_1 = &omap2_nand_write_buf_1;
    272      1.1  jmcneill 	sc->sc_nand_if.write_buf_2 = &omap2_nand_write_buf_2;
    273      1.1  jmcneill 	sc->sc_nand_if.write_1 = &omap2_nand_write_1;
    274      1.1  jmcneill 	sc->sc_nand_if.write_2 = &omap2_nand_write_2;
    275      1.1  jmcneill 	sc->sc_nand_if.busy = &omap2_nand_busy;
    276      1.1  jmcneill 
    277      1.1  jmcneill #ifdef OMAP2_NAND_HARDWARE_ECC
    278      1.1  jmcneill 	omap2_nand_ecc_init(self);
    279      1.1  jmcneill 	sc->sc_nand_if.ecc_compute = &omap2_nand_ecc_compute;
    280      1.1  jmcneill 	sc->sc_nand_if.ecc_correct = &omap2_nand_ecc_correct;
    281      1.1  jmcneill 	sc->sc_nand_if.ecc_prepare = &omap2_nand_ecc_prepare;
    282      1.1  jmcneill 	sc->sc_nand_if.ecc.necc_code_size = 3;
    283      1.1  jmcneill 	sc->sc_nand_if.ecc.necc_block_size = 512;
    284      1.1  jmcneill 	sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_HW;
    285      1.1  jmcneill #else
    286      1.1  jmcneill 	sc->sc_nand_if.ecc.necc_code_size = 3;
    287      1.1  jmcneill 	sc->sc_nand_if.ecc.necc_block_size = 256;
    288      1.1  jmcneill #endif	/* OMAP2_NAND_HARDWARE_ECC */
    289      1.1  jmcneill 
    290      1.1  jmcneill 	if (!pmf_device_register1(sc->sc_dev, NULL, NULL, NULL))
    291      1.1  jmcneill 		aprint_error_dev(sc->sc_dev,
    292      1.1  jmcneill 		    "couldn't establish power handler\n");
    293      1.1  jmcneill 
    294      1.1  jmcneill 	sc->sc_nanddev = nand_attach_mi(&sc->sc_nand_if, sc->sc_dev);
    295      1.1  jmcneill 	if (sc->sc_nanddev == NULL)
    296      1.1  jmcneill 		return;
    297      1.1  jmcneill 
    298      1.1  jmcneill 	for (child = OF_child(phandle); child; child = OF_peer(child)) {
    299      1.1  jmcneill 		if (!fdtbus_status_okay(child))
    300      1.1  jmcneill 			continue;
    301      1.1  jmcneill 
    302      1.1  jmcneill 		if (fdtbus_get_reg(child, 0, &part_addr, &part_size) != 0) {
    303      1.1  jmcneill 			aprint_error_dev(self, "couldn't parse partition %s\n",
    304      1.1  jmcneill 			    fdtbus_get_string(child, "name"));
    305      1.1  jmcneill 			continue;
    306      1.1  jmcneill 		}
    307      1.1  jmcneill 
    308      1.1  jmcneill 		memset(&flash, 0, sizeof(flash));
    309      1.1  jmcneill 		flash.flash_if = &nand_flash_if;
    310      1.1  jmcneill 		flash.partinfo.part_offset = part_addr;
    311      1.1  jmcneill 		flash.partinfo.part_size = part_size;
    312      1.1  jmcneill 		flash.partinfo.part_flags = 0;
    313      1.1  jmcneill 		flash.partinfo.part_name = fdtbus_get_string(child, "label");
    314      1.1  jmcneill 		if (flash.partinfo.part_name == NULL)
    315      1.1  jmcneill 			flash.partinfo.part_name = fdtbus_get_string(child, "name");
    316      1.1  jmcneill 
    317  1.3.2.1   thorpej 		config_found(sc->sc_nanddev, &flash, flash_print, CFARG_EOL);
    318      1.1  jmcneill 	}
    319      1.1  jmcneill }
    320      1.1  jmcneill 
    321      1.1  jmcneill static void
    322      1.1  jmcneill omap2_nand_busy(device_t self)
    323      1.1  jmcneill {
    324      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    325      1.1  jmcneill 
    326      1.1  jmcneill 	while (!(gpmc_register_read(sc, GPMC_STATUS) & WAIT0)) {
    327      1.1  jmcneill 		DELAY(1);
    328      1.1  jmcneill 	}
    329      1.1  jmcneill }
    330      1.1  jmcneill 
    331      1.1  jmcneill static void
    332      1.1  jmcneill omap2_nand_read_1(device_t self, uint8_t *data)
    333      1.1  jmcneill {
    334      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    335      1.1  jmcneill 
    336      1.1  jmcneill 	*data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg);
    337      1.1  jmcneill }
    338      1.1  jmcneill 
    339      1.1  jmcneill static void
    340      1.1  jmcneill omap2_nand_write_1(device_t self, uint8_t data)
    341      1.1  jmcneill {
    342      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    343      1.1  jmcneill 
    344      1.1  jmcneill 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data);
    345      1.1  jmcneill }
    346      1.1  jmcneill 
    347      1.1  jmcneill static void
    348      1.1  jmcneill omap2_nand_read_2(device_t self, uint16_t *data)
    349      1.1  jmcneill {
    350      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    351      1.1  jmcneill 
    352      1.1  jmcneill 	*data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg);
    353      1.1  jmcneill }
    354      1.1  jmcneill 
    355      1.1  jmcneill static void
    356      1.1  jmcneill omap2_nand_write_2(device_t self, uint16_t data)
    357      1.1  jmcneill {
    358      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    359      1.1  jmcneill 
    360      1.1  jmcneill 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data);
    361      1.1  jmcneill }
    362      1.1  jmcneill 
    363      1.1  jmcneill static void
    364      1.1  jmcneill omap2_nand_read_buf_1(device_t self, void *buf, size_t len)
    365      1.1  jmcneill {
    366      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    367      1.1  jmcneill 
    368      1.1  jmcneill 	KASSERT(buf != NULL);
    369      1.1  jmcneill 	KASSERT(len >= 1);
    370      1.1  jmcneill 
    371      1.1  jmcneill 	bus_space_read_multi_1(sc->sc_iot, sc->sc_ioh,
    372      1.1  jmcneill 	    sc->sc_data_reg, buf, len);
    373      1.1  jmcneill }
    374      1.1  jmcneill 
    375      1.1  jmcneill static void
    376      1.1  jmcneill omap2_nand_read_buf_2(device_t self, void *buf, size_t len)
    377      1.1  jmcneill {
    378      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    379      1.1  jmcneill 
    380      1.1  jmcneill 	KASSERT(buf != NULL);
    381      1.1  jmcneill 	KASSERT(len >= 2);
    382      1.1  jmcneill 	KASSERT(!(len & 0x01));
    383      1.1  jmcneill 
    384      1.1  jmcneill 	bus_space_read_multi_2(sc->sc_iot, sc->sc_ioh,
    385      1.1  jmcneill 	    sc->sc_data_reg, buf, len / 2);
    386      1.1  jmcneill }
    387      1.1  jmcneill 
    388      1.1  jmcneill static void
    389      1.1  jmcneill omap2_nand_write_buf_1(device_t self, const void *buf, size_t len)
    390      1.1  jmcneill {
    391      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    392      1.1  jmcneill 
    393      1.1  jmcneill 	KASSERT(buf != NULL);
    394      1.1  jmcneill 	KASSERT(len >= 1);
    395      1.1  jmcneill 
    396      1.1  jmcneill 	bus_space_write_multi_1(sc->sc_iot, sc->sc_ioh,
    397      1.1  jmcneill 	    sc->sc_data_reg, buf, len);
    398      1.1  jmcneill }
    399      1.1  jmcneill 
    400      1.1  jmcneill static void
    401      1.1  jmcneill omap2_nand_write_buf_2(device_t self, const void *buf, size_t len)
    402      1.1  jmcneill {
    403      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    404      1.1  jmcneill 
    405      1.1  jmcneill 	KASSERT(buf != NULL);
    406      1.1  jmcneill 	KASSERT(len >= 2);
    407      1.1  jmcneill 	KASSERT(!(len & 0x01));
    408      1.1  jmcneill 
    409      1.1  jmcneill 	bus_space_write_multi_2(sc->sc_iot, sc->sc_ioh,
    410      1.1  jmcneill 	    sc->sc_data_reg, buf, len / 2);
    411      1.1  jmcneill }
    412      1.1  jmcneill 
    413      1.1  jmcneill #ifdef OMAP2_NAND_HARDWARE_ECC
    414      1.1  jmcneill static uint32_t
    415      1.1  jmcneill convert_ecc(const uint8_t *ecc)
    416      1.1  jmcneill {
    417      1.1  jmcneill 	return ecc[0] | (ecc[1] << 16) | ((ecc[2] & 0xf0) << 20) |
    418      1.1  jmcneill 	    ((ecc[2] & 0x0f) << 8);
    419      1.1  jmcneill }
    420      1.1  jmcneill 
    421      1.1  jmcneill static int
    422      1.1  jmcneill omap2_nand_ecc_init(device_t self)
    423      1.1  jmcneill {
    424      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    425      1.1  jmcneill 	uint32_t val;
    426      1.1  jmcneill 
    427      1.1  jmcneill 	val = gpmc_register_read(sc, GPMC_ECC_CONTROL);
    428      1.1  jmcneill 	/* clear ecc, select ecc register 1 */
    429      1.1  jmcneill 	val &= ~ECCPOINTER;
    430      1.1  jmcneill 	val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1);
    431      1.1  jmcneill 	gpmc_register_write(sc, GPMC_ECC_CONTROL, val);
    432      1.1  jmcneill 
    433      1.1  jmcneill 	/* XXX too many MAGIC */
    434      1.1  jmcneill 	/* set ecc size to 512, set all regs to eccsize1*/
    435      1.1  jmcneill 	val = gpmc_register_read(sc, GPMC_ECC_SIZE_CONFIG);
    436      1.1  jmcneill 	val &= ~ECCSIZE1;
    437      1.1  jmcneill 	val |= MASKEDINT(ECCSIZE1, 512) | 0x0f;
    438      1.1  jmcneill 	gpmc_register_write(sc, GPMC_ECC_CONTROL, val);
    439      1.1  jmcneill 
    440      1.1  jmcneill 	return 0;
    441      1.1  jmcneill }
    442      1.1  jmcneill 
    443      1.1  jmcneill static int
    444      1.1  jmcneill omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc)
    445      1.1  jmcneill {
    446      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    447      1.1  jmcneill 	uint32_t val;
    448      1.1  jmcneill 
    449      1.1  jmcneill 	/* read ecc result register */
    450      1.1  jmcneill 	val = gpmc_register_read(sc, GPMC_ECC1_RESULT);
    451      1.1  jmcneill 
    452      1.1  jmcneill 	ecc[0] = val & 0xff;
    453      1.1  jmcneill 	ecc[1] = (val >> 16) & 0xff;
    454      1.1  jmcneill 	ecc[2] = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
    455      1.1  jmcneill 
    456      1.1  jmcneill 	/* disable ecc engine */
    457      1.1  jmcneill 	val = gpmc_register_read(sc, GPMC_ECC_CONFIG);
    458      1.1  jmcneill 	val &= ~ECCENABLE;
    459      1.1  jmcneill 	gpmc_register_write(sc, GPMC_ECC_CONFIG, val);
    460      1.1  jmcneill 
    461      1.1  jmcneill 	return 0;
    462      1.1  jmcneill }
    463      1.1  jmcneill 
    464      1.1  jmcneill static int
    465      1.1  jmcneill omap2_nand_ecc_prepare(device_t self, int mode)
    466      1.1  jmcneill {
    467      1.1  jmcneill 	struct omap2_nand_softc *sc = device_private(self);
    468      1.1  jmcneill 	uint32_t val;
    469      1.1  jmcneill 
    470      1.1  jmcneill 	/* same for read/write */
    471      1.1  jmcneill 	switch (mode) {
    472      1.1  jmcneill 	case NAND_ECC_READ:
    473      1.1  jmcneill 	case NAND_ECC_WRITE:
    474      1.1  jmcneill 		val = gpmc_register_read(sc, GPMC_ECC_CONTROL);
    475      1.1  jmcneill 		/* clear ecc, select ecc register 1 */
    476      1.1  jmcneill 		val &= ~ECCPOINTER;
    477      1.1  jmcneill 		val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1);
    478      1.1  jmcneill 		gpmc_register_write(sc, GPMC_ECC_CONTROL, val);
    479      1.1  jmcneill 
    480      1.1  jmcneill 		val = gpmc_register_read(sc, GPMC_ECC_CONFIG);
    481      1.1  jmcneill 		val &= ~ECCCS;
    482      1.1  jmcneill 		val |= ECCENABLE | MASKEDINT(ECCCS, sc->sc_cs);
    483      1.1  jmcneill 		if (sc->sc_buswidth == 1)
    484      1.1  jmcneill 			val |= ECC16B;
    485      1.1  jmcneill 		else
    486      1.1  jmcneill 			val &= ~ECC16B;
    487      1.1  jmcneill 		gpmc_register_write(sc, GPMC_ECC_CONFIG, val);
    488      1.1  jmcneill 
    489      1.1  jmcneill 		break;
    490      1.1  jmcneill 	default:
    491      1.1  jmcneill 		aprint_error_dev(self, "invalid i/o mode for ecc prepare\n");
    492      1.1  jmcneill 		return -1;
    493      1.1  jmcneill 	}
    494      1.1  jmcneill 
    495      1.1  jmcneill 	return 0;
    496      1.1  jmcneill }
    497      1.1  jmcneill 
    498      1.1  jmcneill static int
    499      1.1  jmcneill omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc,
    500      1.1  jmcneill     const uint8_t *calcecc)
    501      1.1  jmcneill {
    502      1.1  jmcneill 	uint32_t oecc, cecc, xor;
    503      1.1  jmcneill 	uint16_t parity, offset;
    504      1.1  jmcneill 	uint8_t bit;
    505      1.1  jmcneill 
    506      1.1  jmcneill 	oecc = convert_ecc(oldecc);
    507      1.1  jmcneill 	cecc = convert_ecc(calcecc);
    508      1.1  jmcneill 
    509      1.1  jmcneill 	/* get the difference */
    510      1.1  jmcneill 	xor = oecc ^ cecc;
    511      1.1  jmcneill 
    512      1.1  jmcneill 	/* the data was correct if all bits are zero */
    513      1.1  jmcneill 	if (xor == 0x00)
    514      1.1  jmcneill 		return NAND_ECC_OK;
    515      1.1  jmcneill 
    516      1.1  jmcneill 	switch (popcount32(xor)) {
    517      1.1  jmcneill 	case 12:
    518      1.1  jmcneill 		/* single byte error */
    519      1.1  jmcneill 		parity = xor >> 16;
    520      1.1  jmcneill 		bit = (parity & 0x07);
    521      1.1  jmcneill 		offset = (parity >> 3) & 0x01ff;
    522      1.1  jmcneill 		/* correct bit */
    523      1.1  jmcneill 		data[offset] ^= (0x01 << bit);
    524      1.1  jmcneill 		return NAND_ECC_CORRECTED;
    525      1.1  jmcneill 	case 1:
    526      1.1  jmcneill 		return NAND_ECC_INVALID;
    527      1.1  jmcneill 	default:
    528      1.1  jmcneill 		/* erased page! */
    529      1.1  jmcneill 		if ((oecc == 0x0fff0fff) && (cecc == 0x00000000))
    530      1.1  jmcneill 			return NAND_ECC_OK;
    531      1.1  jmcneill 
    532      1.1  jmcneill 		return NAND_ECC_TWOBIT;
    533      1.1  jmcneill 	}
    534      1.1  jmcneill }
    535      1.1  jmcneill #endif /* !OMAP2_NAND_HARDWARE_ECC */
    536