/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012 Robert N. M. Watson * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int altera_sdcard_ignore_crc_errors = 1; int altera_sdcard_verify_rxtx_writes = 1; /* * Low-level I/O routines for the Altera SD Card University IP Core driver. * * XXXRW: Throughout, it is assumed that the IP Core handles multibyte * registers as little endian, as is the case for other Altera IP cores. * However, the specification makes no reference to endianness, so this * assumption might not always be correct. */ uint16_t altera_sdcard_read_asr(struct altera_sdcard_softc *sc) { return (le16toh(bus_read_2(sc->as_res, ALTERA_SDCARD_OFF_ASR))); } static int altera_sdcard_process_csd0(struct altera_sdcard_softc *sc) { uint64_t c_size, c_size_mult, read_bl_len; uint8_t byte0, byte1, byte2; ALTERA_SDCARD_LOCK_ASSERT(sc); /*- * Compute card capacity per SD Card interface description as follows: * * Memory capacity = BLOCKNR * BLOCK_LEN * * Where: * * BLOCKNR = (C_SIZE + 1) * MULT * MULT = 2^(C_SIZE_MULT+2) * BLOCK_LEN = 2^READ_BL_LEN */ read_bl_len = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_READ_BL_LEN_BYTE]; read_bl_len &= ALTERA_SDCARD_CSD_READ_BL_LEN_MASK; byte0 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_BYTE0]; byte0 &= ALTERA_SDCARD_CSD_C_SIZE_MASK0; byte1 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_BYTE1]; byte2 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_BYTE2]; byte2 &= ALTERA_SDCARD_CSD_C_SIZE_MASK2; c_size = (byte0 >> ALTERA_SDCARD_CSD_C_SIZE_RSHIFT0) | (byte1 << ALTERA_SDCARD_CSD_C_SIZE_LSHIFT1) | (byte2 << ALTERA_SDCARD_CSD_C_SIZE_LSHIFT2); byte0 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE0]; byte0 &= ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK0; byte1 = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE1]; byte1 &= ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK1; c_size_mult = (byte0 >> ALTERA_SDCARD_CSD_C_SIZE_MULT_RSHIFT0) | (byte1 << ALTERA_SDCARD_CSD_C_SIZE_MULT_LSHIFT1); /* * If we're just getting back zero's, mark the card as bad, even * though it could just mean a Very Small Disk Indeed. */ if (c_size == 0 && c_size_mult == 0 && read_bl_len == 0) { device_printf(sc->as_dev, "Ignored zero-size card\n"); return (ENXIO); } sc->as_mediasize = (c_size + 1) * (1 << (c_size_mult + 2)) * (1 << read_bl_len); return (0); } int altera_sdcard_read_csd(struct altera_sdcard_softc *sc) { uint8_t csd_structure; int error; ALTERA_SDCARD_LOCK_ASSERT(sc); /* * XXXRW: Assume for now that when the SD Card IP Core negotiates * voltage/speed/etc, it must use the CSD register, and therefore * populates the SD Card IP Core's cache of the register value. This * means that we can read it without issuing further SD Card commands. * If this assumption proves false, we will (a) get back garbage and * (b) need to add additional states in the driver state machine in * order to query card properties before I/O can start. * * XXXRW: Treating this as an array of bytes, so no byte swapping -- * is that a safe assumption? */ KASSERT(((uintptr_t)&sc->as_csd.csd_data) % 2 == 0, ("%s: CSD buffer unaligned", __func__)); bus_read_region_2(sc->as_res, ALTERA_SDCARD_OFF_CSD, (uint16_t *)sc->as_csd.csd_data, sizeof(sc->as_csd) / 2); /* * Interpret the loaded CSD, extracting certain fields and copying * them into the softc for easy software access. * * Currently, we support only CSD Version 1.0. If we detect a newer * version, suppress card detection. */ csd_structure = sc->as_csd.csd_data[ALTERA_SDCARD_CSD_STRUCTURE_BYTE]; csd_structure &= ALTERA_SDCARD_CSD_STRUCTURE_MASK; csd_structure >>= ALTERA_SDCARD_CSD_STRUCTURE_RSHIFT; sc->as_csd_structure = csd_structure; /* * Interpret the CSD field based on its version. Extract fields, * especially mediasize. * * XXXRW: Desirable to support further CSD versions here. */ switch (sc->as_csd_structure) { case 0: error = altera_sdcard_process_csd0(sc); if (error) return (error); break; default: device_printf(sc->as_dev, "Ignored disk with unsupported CSD structure (%d)\n", sc->as_csd_structure); return (ENXIO); } return (0); } /* * XXXRW: The Altera IP Core specification indicates that RR1 is a 16-bit * register, but all bits it identifies are >16 bit. Most likely, RR1 is a * 32-bit register? */ static uint16_t altera_sdcard_read_rr1(struct altera_sdcard_softc *sc) { return (le16toh(bus_read_2(sc->as_res, ALTERA_SDCARD_OFF_RR1))); } static void altera_sdcard_write_cmd_arg(struct altera_sdcard_softc *sc, uint32_t cmd_arg) { bus_write_4(sc->as_res, ALTERA_SDCARD_OFF_CMD_ARG, htole32(cmd_arg)); } static void altera_sdcard_write_cmd(struct altera_sdcard_softc *sc, uint16_t cmd) { bus_write_2(sc->as_res, ALTERA_SDCARD_OFF_CMD, htole16(cmd)); } static void altera_sdcard_read_rxtx_buffer(struct altera_sdcard_softc *sc, void *data, size_t len) { KASSERT((uintptr_t)data % 2 == 0, ("%s: unaligned data %p", __func__, data)); KASSERT((len <= ALTERA_SDCARD_SECTORSIZE) && (len % 2 == 0), ("%s: invalid length %ju", __func__, len)); bus_read_region_2(sc->as_res, ALTERA_SDCARD_OFF_RXTX_BUFFER, (uint16_t *)data, len / 2); } static void altera_sdcard_write_rxtx_buffer(struct altera_sdcard_softc *sc, void *data, size_t len) { u_int corrections, differences, i, retry_counter; uint16_t d, v; KASSERT((uintptr_t)data % 2 == 0, ("%s: unaligned data %p", __func__, data)); KASSERT((len <= ALTERA_SDCARD_SECTORSIZE) && (len % 2 == 0), ("%s: invalid length %ju", __func__, len)); retry_counter = 0; do { bus_write_region_2(sc->as_res, ALTERA_SDCARD_OFF_RXTX_BUFFER, (uint16_t *)data, len / 2); /* * XXXRW: Due to a possible hardware bug, the above call to * bus_write_region_2() might not succeed. If the workaround * is enabled, verify each write and retry until it succeeds. * * XXXRW: Do we want a limit counter for retries here? */ recheck: corrections = 0; differences = 0; if (altera_sdcard_verify_rxtx_writes) { for (i = 0; i < ALTERA_SDCARD_SECTORSIZE; i += 2) { v = bus_read_2(sc->as_res, ALTERA_SDCARD_OFF_RXTX_BUFFER + i); d = *(uint16_t *)((uint8_t *)data + i); if (v != d) { if (retry_counter == 0) { bus_write_2(sc->as_res, ALTERA_SDCARD_OFF_RXTX_BUFFER + i, d); v = bus_read_2(sc->as_res, ALTERA_SDCARD_OFF_RXTX_BUFFER + i); if (v == d) { corrections++; device_printf(sc->as_dev, "%s: single word rewrite worked" " at offset %u\n", __func__, i); continue; } } differences++; device_printf(sc->as_dev, "%s: retrying write -- difference" " %u at offset %u, retry %u\n", __func__, differences, i, retry_counter); } } if (differences != 0) { retry_counter++; if (retry_counter == 1 && corrections == differences) goto recheck; } } } while (differences != 0); if (retry_counter) device_printf(sc->as_dev, "%s: succeeded after %u retries\n", __func__, retry_counter); } static void altera_sdcard_io_start_internal(struct altera_sdcard_softc *sc, struct bio **bpp) { struct bio *bp; bp = *bpp; switch (bp->bio_cmd) { case BIO_READ: altera_sdcard_write_cmd_arg(sc, bp->bio_pblkno * ALTERA_SDCARD_SECTORSIZE); altera_sdcard_write_cmd(sc, ALTERA_SDCARD_CMD_READ_BLOCK); break; case BIO_WRITE: altera_sdcard_write_rxtx_buffer(sc, bp->bio_data, bp->bio_bcount); altera_sdcard_write_cmd_arg(sc, bp->bio_pblkno * ALTERA_SDCARD_SECTORSIZE); altera_sdcard_write_cmd(sc, ALTERA_SDCARD_CMD_WRITE_BLOCK); break; default: biofinish(bp, NULL, EOPNOTSUPP); *bpp = NULL; } } void altera_sdcard_io_start(struct altera_sdcard_softc *sc, struct bio *bp) { ALTERA_SDCARD_LOCK_ASSERT(sc); KASSERT(sc->as_currentbio == NULL, ("%s: bio already started", __func__)); /* * We advertise a block size and maximum I/O size up the stack of the * SD Card IP Core sector size. Catch any attempts to not follow the * rules. */ KASSERT(bp->bio_bcount == ALTERA_SDCARD_SECTORSIZE, ("%s: I/O size not %d", __func__, ALTERA_SDCARD_SECTORSIZE)); altera_sdcard_io_start_internal(sc, &bp); sc->as_currentbio = bp; sc->as_retriesleft = ALTERA_SDCARD_RETRY_LIMIT; } /* * Handle completed I/O. ASR is passed in to avoid reading it more than once. * Return 1 if the I/O is actually complete (success, or retry limit * exceeded), or 0 if not. */ int altera_sdcard_io_complete(struct altera_sdcard_softc *sc, uint16_t asr) { struct bio *bp; uint16_t rr1, mask; int error; ALTERA_SDCARD_LOCK_ASSERT(sc); KASSERT(!(asr & ALTERA_SDCARD_ASR_CMDINPROGRESS), ("%s: still in progress", __func__)); KASSERT(asr & ALTERA_SDCARD_ASR_CARDPRESENT, ("%s: card removed", __func__)); bp = sc->as_currentbio; /*- * Handle I/O retries if an error is returned by the device. Various * quirks handled in the process: * * 1. ALTERA_SDCARD_ASR_CMDDATAERROR is ignored for BIO_WRITE. * 2. ALTERA_SDCARD_RR1_COMMANDCRCFAILED is optionally ignored for * BIO_READ. */ error = 0; rr1 = altera_sdcard_read_rr1(sc); switch (bp->bio_cmd) { case BIO_READ: mask = ALTERA_SDCARD_RR1_ERRORMASK; if (altera_sdcard_ignore_crc_errors) mask &= ~ALTERA_SDCARD_RR1_COMMANDCRCFAILED; if (asr & ALTERA_SDCARD_ASR_CMDTIMEOUT) error = EIO; else if ((asr & ALTERA_SDCARD_ASR_CMDDATAERROR) && (rr1 & mask)) error = EIO; else error = 0; break; case BIO_WRITE: if (asr & ALTERA_SDCARD_ASR_CMDTIMEOUT) error = EIO; else error = 0; break; default: break; } if (error) { sc->as_retriesleft--; if (sc->as_retriesleft == 0 || bootverbose) device_printf(sc->as_dev, "%s: %s operation block %ju " "length %ju failed; asr 0x%08x (rr1: 0x%04x)%s\n", __func__, bp->bio_cmd == BIO_READ ? "BIO_READ" : (bp->bio_cmd == BIO_WRITE ? "BIO_WRITE" : "unknown"), bp->bio_pblkno, bp->bio_bcount, asr, rr1, sc->as_retriesleft != 0 ? " retrying" : ""); /* * This attempt experienced an error; possibly retry. */ if (sc->as_retriesleft != 0) { sc->as_flags |= ALTERA_SDCARD_FLAG_IOERROR; altera_sdcard_io_start_internal(sc, &bp); return (0); } sc->as_flags &= ~ALTERA_SDCARD_FLAG_IOERROR; } else { /* * Successful I/O completion path. */ if (sc->as_flags & ALTERA_SDCARD_FLAG_IOERROR) { device_printf(sc->as_dev, "%s: %s operation block %ju" " length %ju succeeded after %d retries\n", __func__, bp->bio_cmd == BIO_READ ? "BIO_READ" : (bp->bio_cmd == BIO_WRITE ? "write" : "unknown"), bp->bio_pblkno, bp->bio_bcount, ALTERA_SDCARD_RETRY_LIMIT - sc->as_retriesleft); sc->as_flags &= ~ALTERA_SDCARD_FLAG_IOERROR; } switch (bp->bio_cmd) { case BIO_READ: altera_sdcard_read_rxtx_buffer(sc, bp->bio_data, bp->bio_bcount); break; case BIO_WRITE: break; default: panic("%s: unsupported I/O operation %d", __func__, bp->bio_cmd); } bp->bio_resid = 0; error = 0; } biofinish(bp, NULL, error); sc->as_currentbio = NULL; return (1); }