Index: sys/arm/at91/at91_mci.c =================================================================== --- sys/arm/at91/at91_mci.c (revision 234999) +++ sys/arm/at91/at91_mci.c (working copy) @@ -699,6 +699,9 @@ switch (which) { default: return (EINVAL); + case MMCBR_IVAR_BUS_TYPE: + *(int *)result = bus_type_sd; + break; case MMCBR_IVAR_BUS_MODE: *(int *)result = sc->host.ios.bus_mode; break; @@ -782,6 +785,7 @@ sc->host.ios.vdd = value; break; /* These are read-only */ + case MMCBR_IVAR_BUS_TYPE: case MMCBR_IVAR_CAPS: case MMCBR_IVAR_HOST_OCR: case MMCBR_IVAR_F_MIN: Index: sys/conf/files =================================================================== --- sys/conf/files (revision 234999) +++ sys/conf/files (working copy) @@ -1606,6 +1606,7 @@ dev/mmc/mmcbr_if.m standard dev/mmc/mmcbus_if.m standard dev/mmc/mmcsd.c optional mmcsd +dev/mmc/mmcspi.c optional mmcspi dev/mn/if_mn.c optional mn pci dev/mps/mps.c optional mps dev/mps/mps_config.c optional mps Index: sys/modules/mmcspi/Makefile =================================================================== --- sys/modules/mmcspi/Makefile (revision 0) +++ sys/modules/mmcspi/Makefile (revision 0) @@ -0,0 +1,7 @@ + +.PATH: ${.CURDIR}/../../dev/mmc + +KMOD= mmcspi +SRCS= mmcspi.c mmcbr_if.h spibus_if.h device_if.h bus_if.h + +.include Index: sys/dev/mmc/mmcbrvar.h =================================================================== --- sys/dev/mmc/mmcbrvar.h (revision 234999) +++ sys/dev/mmc/mmcbrvar.h (working copy) @@ -60,6 +60,7 @@ #include "mmcbr_if.h" enum mmcbr_device_ivars { + MMCBR_IVAR_BUS_TYPE, MMCBR_IVAR_BUS_MODE, MMCBR_IVAR_BUS_WIDTH, MMCBR_IVAR_CHIP_SELECT, @@ -82,6 +83,7 @@ #define MMCBR_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(mmcbr, var, MMCBR, ivar, type) +MMCBR_ACCESSOR(bus_type, BUS_TYPE, int) MMCBR_ACCESSOR(bus_mode, BUS_MODE, int) MMCBR_ACCESSOR(bus_width, BUS_WIDTH, int) MMCBR_ACCESSOR(chip_select, CHIP_SELECT, int) Index: sys/dev/mmc/mmcreg.h =================================================================== --- sys/dev/mmc/mmcreg.h (revision 234999) +++ sys/dev/mmc/mmcreg.h (working copy) @@ -78,6 +78,7 @@ #define MMC_CMD_BC (2ul << 5) /* Broadcast command, no response */ #define MMC_CMD_BCR (3ul << 5) /* Broadcast command with response */ #define MMC_CMD_MASK (3ul << 5) +#define MMC_CMD_IS_APP (1ul << 8) /* Command is being sent as an APP command */ /* Possible response types defined in the standard: */ #define MMC_RSP_NONE (0) Index: sys/dev/mmc/bridge.h =================================================================== --- sys/dev/mmc/bridge.h (revision 234999) +++ sys/dev/mmc/bridge.h (working copy) @@ -100,6 +100,10 @@ cs_dontcare = 0, cs_high, cs_low }; +enum mmc_bus_type { + bus_type_sd = 0, bus_type_spi +}; + enum mmc_bus_width { bus_width_1 = 0, bus_width_4 = 2, bus_width_8 = 3 }; @@ -111,6 +115,7 @@ struct mmc_ios { uint32_t clock; /* Speed of the clock in Hz to move data */ enum mmc_vdd vdd; /* Voltage to apply to the power pins/ */ + enum mmc_bus_type bus_type; enum mmc_bus_mode bus_mode; enum mmc_chip_select chip_select; enum mmc_bus_width bus_width; Index: sys/dev/mmc/mmcspi.c =================================================================== --- sys/dev/mmc/mmcspi.c (revision 0) +++ sys/dev/mmc/mmcspi.c (revision 0) @@ -0,0 +1,2559 @@ +/*- + * Copyright (c) 2012 Patrick Kelsey. 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 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 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. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * CRC routines adapted from public domain code written by Lammert Bies. + * + * + * This is an implementation of mmcbr that communicates with SD/MMC cards in + * SPI mode via spibus_if. In order to minimize changes to the existing + * MMC/SD stack (and allow for maximal reuse of the same), the behavior of + * the SD-bus command set is emulated as much as possible, where required. + * + * The SPI bus ownership behavior is to acquire the SPI bus for the entire + * duration that the MMC host is acquired. + * + * CRC checking is enabled by default, but can be disabled at runtime + * per-card via sysctl (e.g. sysctl dev.mmcspi.0.use_crc=0). + * + * Considered, but not implemented: + * - Card presence detection + * - Card power control + * - Detection of lock switch state on cards that have them + * - Yielding the CPU during long card busy cycles + * + * Originally developed and tested using a MicroTik RouterBOARD RB450G and + * the 31 microSD cards listed at the end of this file. + */ + +#include +__FBSDID("$FreeBSD$"); + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#include "mmcbr_if.h" +#include "spibus_if.h" + +#define MMCSPI_RETRIES 3 /* number of retries for internally originated commands */ +#define MMCSPI_TIMEOUT_SEC 3 + +#define MMCSPI_MAX_RSP_LEN 5 /* max length of an Rn response */ +#define MMCSPI_OCR_LEN 4 + +#define MMCSPI_DATA_BLOCK_LEN 512 +#define MMCSPI_DATA_CRC_LEN 2 + +#define MMCSPI_POLL_LEN 8 /* amount to read when searching */ + +#define MMCSPI_R1_MASK 0x80 /* mask used to search for R1 tokens */ +#define MMCSPI_R1_VALUE 0x00 /* value used to search for R1 tokens */ +#define MMCSPI_DR_MASK 0x11 /* mask used to search for data response tokens */ +#define MMCSPI_DR_VALUE 0x01 /* value used to search for data response tokens */ + +#define MMCSPI_DR_ERR_MASK 0x0e +#define MMCSPI_DR_ERR_NONE 0x04 +#define MMCSPI_DR_ERR_CRC 0x0a +#define MMCSPI_DR_ERR_WRITE 0x0c + +#define MMCSPI_TOKEN_SB 0xfe /* start block token for read single, + read multi, and write single */ +#define MMCSPI_TOKEN_SB_WM 0xfc /* start block token for write multi */ +#define MMCSPI_TOKEN_ST 0xfd /* stop transmission token */ +#define MMCSPI_IS_DE_TOKEN(x) (0 == ((x) & 0xf0)) /* detector for data error token */ + + +#define MMCSPI_R1_IDLE 0x01 +#define MMCSPI_R1_ERASE_RST 0x02 +#define MMCSPI_R1_ILL_CMD 0x04 +#define MMCSPI_R1_CRC_ERR 0x08 +#define MMCSPI_R1_ERASE_ERR 0x10 +#define MMCSPI_R1_ADDR_ERR 0x20 +#define MMCSPI_R1_PARAM_ERR 0x40 + +#define MMCSPI_R1_ERR_MASK (MMCSPI_R1_PARAM_ERR | MMCSPI_R1_ADDR_ERR | \ + MMCSPI_R1_ERASE_ERR | MMCSPI_R1_CRC_ERR | \ + MMCSPI_R1_ILL_CMD) + +#define MMCSPI_R2_LOCKED 0x01 +#define MMCSPI_R2_WP_ER_LCK 0x02 +#define MMCSPI_R2_ERR 0x04 +#define MMCSPI_R2_CC_ERR 0x08 +#define MMCSPI_R2_ECC_FAIL 0x10 +#define MMCSPI_R2_WP_VIOLATE 0x20 +#define MMCSPI_R2_ERASE_PARAM 0x40 +#define MMCSPI_R2_OOR_CSD_OW 0x80 + +/* commands that only apply to the SPI interface */ +#define MMCSPI_READ_OCR 58 +#define MMCSPI_CRC_ON_OFF 59 + + +struct mmcspi_command { + struct mmc_command *mmc_cmd; /* command passed from mmc layer */ + uint32_t opcode; /* possibly translated opcode */ + uint32_t arg; /* possibly translated arg */ + uint32_t flags; /* possibly translated flags */ + uint32_t retries; /* possibly translated retry count */ + struct mmc_data *data; /* possibly redirected data segment */ + unsigned int error_mask; /* mask to use when checking for R1 errors */ + unsigned char use_crc; /* do crc checking for this command */ + unsigned char rsp_type; /* type of response to this command */ +#define MMCSPI_RSP_R1 0 +#define MMCSPI_RSP_R1B 1 +#define MMCSPI_RSP_R2 2 +#define MMCSPI_RSP_R3 3 +#define MMCSPI_RSP_R7 4 + unsigned char rsp_len; /* length of response to this command */ + struct mmc_data ldata; /* local read data */ +}; + +struct mmcspi_slot { + struct mmcspi_softc *sc; /* back pointer to parent bridge */ + device_t dev; /* mmc device for slot */ + boolean_t bus_busy; /* indicates whether host has been acquired */ + struct mmc_host host; /* host parameters */ + struct mtx mtx; /* slot mutex */ + uint8_t last_ocr[MMCSPI_OCR_LEN]; /* last ocr retrieved after CMD8 */ + uint32_t last_opcode; /* last opcode requested by mmc layer */ + uint32_t last_flags; /* last flags requested by mmc layer */ + unsigned int crc_enabled; /* whether crc checking is enabled on the card */ + unsigned int crc_init_done; /* whether the initial crc setting has + been sent to the card */ +#define MMCSPI_MAX_LDATA_LEN 16 + uint8_t ldata_buf[MMCSPI_MAX_LDATA_LEN]; +}; + + +struct mmcspi_softc { + device_t dev; /* this mmc bridge device */ + device_t busdev; /* spi bus device this bridge is on */ + struct mmcspi_slot slot; /* state for the single supported slot */ + unsigned int use_crc; /* whether to enable command CRC checking */ +}; + + +/* device interface */ +static int mmcspi_probe(device_t dev); +static int mmcspi_attach(device_t dev); +static int mmcspi_detach(device_t dev); +static int mmcspi_suspend(device_t dev); +static int mmcspi_resume(device_t dev); + +/* bus interface */ +static int mmcspi_read_ivar(device_t bus, device_t child, int which, + uintptr_t *result); +static int mmcspi_write_ivar(device_t bus, device_t child, int which, + uintptr_t value); + +/* mmcbr interface */ +static int mmcspi_update_ios(device_t brdev, device_t reqdev); +static int mmcspi_request(device_t brdev, device_t reqdev, + struct mmc_request *req); +static int mmcspi_get_ro(device_t brdev, device_t reqdev); +static int mmcspi_acquire_host(device_t brdev, device_t reqdev); +static int mmcspi_release_host(device_t brdev, device_t reqdev); + +/* supporting cast */ +static void mmcspi_slot_init(device_t brdev, struct mmcspi_slot *slot); +static void mmcspi_slot_fini(device_t brdev, struct mmcspi_slot *slot); + +static void mmcspi_card_add(struct mmcspi_slot *slot); +static void mmcspi_card_delete(struct mmcspi_slot *slot); + +static unsigned int mmcspi_do_spi_read(device_t dev, uint8_t *data, + unsigned int len); +static unsigned int mmcspi_do_spi_write(device_t dev, uint8_t *cmd, + unsigned int cmdlen, uint8_t *data, + unsigned int datalen); + +static unsigned int mmcspi_wait_for_not_busy(device_t dev); +static unsigned int mmcspi_shift_copy(uint8_t *dest, uint8_t *src, + unsigned int src_len, unsigned int shift); +static unsigned int mmcspi_get_response_token(device_t dev, uint8_t mask, + uint8_t value, unsigned int len, + unsigned int has_busy, + uint8_t *rspbuf); + +static unsigned int mmcspi_set_up_command(device_t dev, + struct mmcspi_command *mmcspi_cmd, + struct mmc_command *mmc_cmd); +static unsigned int mmcspi_send_cmd(device_t dev, struct mmcspi_command *cmd, + uint8_t *rspbuf); + +static unsigned int mmcspi_read_block(device_t dev, uint8_t *data, + unsigned int len, unsigned int check_crc16, + unsigned int check_crc7); +static unsigned int mmcspi_send_stop(device_t dev, unsigned int retries); +static unsigned int mmcspi_read_phase(device_t dev, struct mmcspi_command *cmd); +static unsigned int mmcspi_write_block(device_t dev, uint8_t *data, + unsigned int is_multi, + unsigned char use_crc, uint8_t *status); +static unsigned int mmcspi_write_phase(device_t dev, struct mmcspi_command *cmd); +static unsigned int mmcspi_translate_response(device_t dev, + struct mmcspi_command *cmd, + uint8_t *rspbuf); +static unsigned int mmcspi_get_ocr(device_t dev, uint8_t *ocrbuf); +static unsigned int mmcspi_set_crc_on_off(device_t dev, unsigned int crc_on); +static unsigned int mmcspi_update_crc_setting(device_t dev, unsigned int crc_on); + +static uint8_t update_crc7(uint8_t crc, uint8_t *buf, unsigned int len); +static uint16_t update_crc16(uint16_t crc, uint8_t *buf, unsigned int len); +static void init_crc7tab(void); +static void init_crc16tab(void); + +static int mmcspi_modevent_handler(module_t mod, int what, void *arg); + +#if defined(MMCSPI_ENABLE_DEBUG_FUNCS) +static void mmcspi_dump_data(device_t dev, const char *label, uint8_t *data, + unsigned int len); +static void mmcspi_dump_spi_bus(device_t dev, unsigned int len); +#endif + + +#define MMCSPI_LOCK_SLOT(_slot) mtx_lock(&(_slot)->mtx) +#define MMCSPI_UNLOCK_SLOT(_slot) mtx_unlock(&(_slot)->mtx) +#define MMCSPI_SLOT_LOCK_INIT(_slot) \ + mtx_init(&(_slot)->mtx, "SD slot mtx", "mmcspi", MTX_DEF) +#define MMCSPI_SLOT_LOCK_DESTROY(_slot) mtx_destroy(&(_slot)->mtx); +#define MMCSPI_ASSERT_SLOT_LOCKED(_slot) mtx_assert(&(_slot)->mtx, MA_OWNED); +#define MMCSPI_ASSERT_SLOT_UNLOCKED(_slot) mtx_assert(&(_slot)->mtx, MA_NOTOWNED); + + +#define TRACE_ZONE_ENABLED(zone) (trace_zone_mask & TRACE_ZONE_##zone) + +#define TRACE_ENTER(dev) \ + if (TRACE_ZONE_ENABLED(ENTER)) { \ + device_printf(dev, "%s: enter\n", __func__); \ + } + +#define TRACE_EXIT(dev) \ + if (TRACE_ZONE_ENABLED(EXIT)) { \ + device_printf(dev, "%s: exit\n", __func__); \ + } + +#define TRACE(dev, zone, ...) \ + if (TRACE_ZONE_ENABLED(zone)) { \ + device_printf(dev, __VA_ARGS__); \ + } + + +#define TRACE_ZONE_ENTER (1ul << 0) /* function entrance */ +#define TRACE_ZONE_EXIT (1ul << 1) /* function exit */ +#define TRACE_ZONE_ACTION (1ul << 2) /* for narrating major actions taken */ +#define TRACE_ZONE_RESULT (1ul << 3) /* for narrating results of actions */ +#define TRACE_ZONE_ERROR (1ul << 4) /* for reporting errors */ +#define TRACE_ZONE_DATA (1ul << 5) /* for dumping bus data */ +#define TRACE_ZONE_DETAILS (1ul << 6) /* for narrating minor actions and results */ + +#define TRACE_ZONE_NONE 0 +#define TRACE_ZONE_ALL 0xffffffff + + +#define CRC7_INITIAL 0x00 +#define CRC16_INITIAL 0x0000 + + +SYSCTL_NODE(_hw, OID_AUTO, mmcspi, CTLFLAG_RD, 0, "mmcspi driver"); + + +static unsigned int trace_zone_mask = TRACE_ZONE_ERROR; + +static uint8_t crc7tab[256]; +static uint16_t crc16tab[256]; +static uint8_t onesbuf[MMCSPI_DATA_BLOCK_LEN]; /* for driving the tx line + when receiving */ +static uint8_t junkbuf[MMCSPI_DATA_BLOCK_LEN]; /* for receiving data when + transmitting */ + +static int +mmcspi_probe(device_t dev) +{ + TRACE_ENTER(dev); + + device_set_desc(dev, "mmc spi-mode bridge"); + + TRACE_EXIT(dev); + return (0); +} + + +static void +mmcspi_slot_init(device_t brdev, struct mmcspi_slot *slot) +{ + TRACE_ENTER(brdev); + + struct mmcspi_softc *sc = device_get_softc(brdev); + struct spi_config cfg; + + slot->sc = sc; + slot->dev = NULL; /* will get real value when card is added */ + slot->bus_busy = false; + slot->host.f_min = 100000; /* this should be as low as we need to go + for any card */ + slot->host.caps = 0; + + SPIBUS_ACQUIRE_BUS(sc->busdev, sc->dev); + + cfg.clock_hz = 0; + SPIBUS_SET_CONFIG(sc->busdev, &cfg); + + SPIBUS_GET_CONFIG(sc->busdev, &cfg); + slot->host.f_max = cfg.clock_hz; + + if (slot->host.f_max > 25000000) { + slot->host.caps |= MMC_CAP_HSPEED; + } + + SPIBUS_RELEASE_BUS(sc->busdev, sc->dev); + + /* SPI mode requires 3.3V operation */ + slot->host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; + + MMCSPI_SLOT_LOCK_INIT(slot); + + TRACE_EXIT(brdev); +} + + +static void +mmcspi_slot_fini(device_t brdev, struct mmcspi_slot *slot) +{ + TRACE_ENTER(brdev); + + MMCSPI_SLOT_LOCK_DESTROY(slot); + + TRACE_EXIT(brdev); +} + + +static void +mmcspi_card_add(struct mmcspi_slot *slot) +{ + device_t brdev = slot->sc->dev; + + TRACE_ENTER(brdev); + + MMCSPI_LOCK_SLOT(slot); + slot->dev = device_add_child(brdev, "mmc", -1); + device_set_ivars(slot->dev, slot); + MMCSPI_UNLOCK_SLOT(slot); + + device_probe_and_attach(slot->dev); + + TRACE_EXIT(brdev); +} + + +static void +mmcspi_card_delete(struct mmcspi_slot *slot) +{ + device_t brdev = slot->sc->dev; + device_t dev; + + TRACE_ENTER(brdev); + + MMCSPI_LOCK_SLOT(slot); + dev = slot->dev; + slot->dev = NULL; + MMCSPI_UNLOCK_SLOT(slot); + device_delete_child(brdev, dev); + + TRACE_EXIT(brdev); +} + + +static int +mmcspi_attach(device_t dev) +{ + struct mmcspi_softc *sc = device_get_softc(dev); + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); + struct sysctl_oid *tree = device_get_sysctl_tree(dev); + struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); + + TRACE_ENTER(dev); + + sc->dev = dev; + sc->busdev = device_get_parent(dev); + sc->use_crc = 1; + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, + "use_crc", CTLFLAG_RW, &sc->use_crc, sizeof(sc->use_crc), + "Enable/disable crc checking"); + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, + "trace_mask", CTLFLAG_RW, &trace_zone_mask, + sizeof(trace_zone_mask), + "Bitmask for adjusting trace messages"); + + mmcspi_slot_init(dev, &sc->slot); + + /* XXX trigger this from card insert detection */ + mmcspi_card_add(&sc->slot); + + TRACE_EXIT(dev); + return (0); +} + + +static int +mmcspi_detach(device_t dev) +{ + TRACE_ENTER(dev); + + struct mmcspi_softc *sc = device_get_softc(dev); + + /* XXX trigger this from card removal detection */ + mmcspi_card_delete(&sc->slot); + + mmcspi_slot_fini(dev, &sc->slot); + + TRACE_EXIT(dev); + + return (0); +} + + +static int +mmcspi_suspend(device_t dev) +{ + int err; + TRACE_ENTER(dev); + + err = bus_generic_suspend(dev); + if (err) { + TRACE_EXIT(dev); + return (err); + } + + TRACE_EXIT(dev); + return (0); +} + + +static int +mmcspi_resume(device_t dev) +{ + int err; + TRACE_ENTER(dev); + + err = bus_generic_resume(dev); + if (err) { + TRACE_EXIT(dev); + return (err); + } + + TRACE_EXIT(dev); + return (0); +} + + +static int +mmcspi_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) +{ + TRACE_ENTER(bus); + + struct mmcspi_slot *slot = device_get_ivars(child); + + switch (which) { + case MMCBR_IVAR_BUS_TYPE: + *result = bus_type_spi; + break; + case MMCBR_IVAR_BUS_MODE: + *result = slot->host.ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *result = slot->host.ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *result = slot->host.ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *result = slot->host.ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *result = slot->host.f_min; + break; + case MMCBR_IVAR_F_MAX: + *result = slot->host.f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *result = slot->host.host_ocr; + break; + case MMCBR_IVAR_MODE: + *result = slot->host.mode; + break; + case MMCBR_IVAR_OCR: + *result = slot->host.ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *result = slot->host.ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *result = slot->host.ios.vdd; + break; + case MMCBR_IVAR_CAPS: + *result = slot->host.caps; + break; + case MMCBR_IVAR_TIMING: + *result = slot->host.ios.timing; + break; + case MMCBR_IVAR_MAX_DATA: + *result = 64*1024; /* seems reasonable, not dictated by anything */ + break; + default: + return (EINVAL); + } + + TRACE_EXIT(bus); + + return (0); +} + + +static int +mmcspi_write_ivar(device_t bus, device_t child, int which, uintptr_t value) +{ + TRACE_ENTER(bus); + + struct mmcspi_slot *slot = device_get_ivars(child); + + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + slot->host.ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + slot->host.ios.bus_width = value; + break; + case MMCBR_IVAR_CLOCK: + slot->host.ios.clock = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + slot->host.ios.chip_select = value; + break; + case MMCBR_IVAR_MODE: + slot->host.mode = value; + break; + case MMCBR_IVAR_OCR: + slot->host.ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + slot->host.ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + slot->host.ios.vdd = value; + break; + case MMCBR_IVAR_TIMING: + slot->host.ios.timing = value; + break; + case MMCBR_IVAR_BUS_TYPE: + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + return (EINVAL); + } + TRACE_EXIT(bus); + + return (0); +} + + +static int +mmcspi_update_ios(device_t brdev, device_t reqdev) +{ + TRACE_ENTER(brdev); + struct mmcspi_softc *sc = device_get_softc(brdev); + struct mmcspi_slot *slot = device_get_ivars(reqdev); + struct spi_config cfg; + struct spi_command spi_cmd; + + if (power_up == slot->host.ios.power_mode) { + /* + * This sequence provides the initialization steps required + * by the spec after card power is applied, but before any + * commands are issued. These operations are harmless if + * applied at any other time (after a warm reset, for + * example). + */ + + /* + * XXX Power-on portion of implementation of card power + * control should go here. Should probably include a power + * off first to ensure card is fully reset from any previous + * state. + */ + + /* + * Make sure power to card has ramped up. The spec requires + * power to ramp up in 35ms or less. + */ + DELAY(35000); + + /* + * Provide at least 74 clocks with CS and MOSI high that the + * spec requires after card power stabilizes. + */ + + /* Use 100 kHz for maximum compatibility. */ + cfg.clock_hz = 100000; + SPIBUS_SET_CONFIG(sc->busdev, &cfg); + SPIBUS_GET_CONFIG(sc->busdev, &cfg); + slot->host.ios.clock = cfg.clock_hz; + + spi_cmd.tx_cmd = onesbuf; + spi_cmd.rx_cmd = junkbuf; + spi_cmd.tx_cmd_sz = spi_cmd.rx_cmd_sz = 10; + spi_cmd.tx_data = spi_cmd.rx_data = NULL; + spi_cmd.tx_data_sz = spi_cmd.rx_data_sz = 0; + spi_cmd.flags = SPI_SKIP_CHIP_SELECT | SPI_CHIP_SELECT_HIGH; + + SPIBUS_TRANSFER(sc->busdev, sc->dev, &spi_cmd); + + /* + * Perhaps this was a warm reset and the card is in the + * middle of a long operation. + */ + mmcspi_wait_for_not_busy(brdev); + + slot->last_opcode = 0xffffffff; + slot->last_flags = 0; + memset(slot->last_ocr, 0, MMCSPI_OCR_LEN); + slot->crc_enabled = 0; + slot->crc_init_done = 0; + } + + + /* + * A clock value of zero means set the clock line low, which is the + * normal spi idle state. + */ + if (slot->host.ios.clock > 0) { + cfg.clock_hz = slot->host.ios.clock; + SPIBUS_SET_CONFIG(sc->busdev, &cfg); + + /* retrieve the possibly adjusteed downward setting */ + SPIBUS_GET_CONFIG(sc->busdev, &cfg); + slot->host.ios.clock = cfg.clock_hz; + } + + if (power_off == slot->host.ios.power_mode) { + /* + * XXX Power-off portion of implementation of card power + * control should go here. + */ + } + + TRACE_EXIT(brdev); + + return (0); +} + + +static unsigned int +mmcspi_do_spi_read(device_t dev, uint8_t *data, unsigned int len) +{ + TRACE_ENTER(dev); + + struct mmcspi_softc *sc = device_get_softc(dev); + struct spi_command spi_cmd; + int err; + + spi_cmd.tx_cmd = onesbuf; + spi_cmd.rx_cmd = data; + spi_cmd.tx_cmd_sz = spi_cmd.rx_cmd_sz = len; + spi_cmd.tx_data = NULL; + spi_cmd.rx_data = NULL; + spi_cmd.tx_data_sz = spi_cmd.rx_data_sz = 0; + spi_cmd.flags = SPI_SKIP_CHIP_SELECT; + + err = SPIBUS_TRANSFER(sc->busdev, sc->dev, &spi_cmd); + + TRACE_EXIT(dev); + return (err ? MMC_ERR_FAILED : MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_do_spi_write(device_t dev, uint8_t *cmd, unsigned int cmdlen, + uint8_t *data, unsigned int datalen) +{ + TRACE_ENTER(dev); + + struct mmcspi_softc *sc = device_get_softc(dev); + struct spi_command spi_cmd; + int err; + + spi_cmd.tx_cmd = cmd; + spi_cmd.rx_cmd = junkbuf; + spi_cmd.tx_cmd_sz = spi_cmd.rx_cmd_sz = cmdlen; + spi_cmd.tx_data = data; + spi_cmd.rx_data = junkbuf; + spi_cmd.tx_data_sz = spi_cmd.rx_data_sz = datalen; + spi_cmd.flags = SPI_SKIP_CHIP_SELECT; + + err = SPIBUS_TRANSFER(sc->busdev, sc->dev, &spi_cmd); + + TRACE_EXIT(dev); + return (err ? MMC_ERR_FAILED : MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_wait_for_not_busy(device_t dev) +{ + TRACE_ENTER(dev); + + int i; + unsigned int err; + unsigned int busy_length = 0; + uint8_t pollbuf[MMCSPI_POLL_LEN]; + struct bintime start, elapsed; + + TRACE(dev, ACTION, "waiting for not busy\n"); + + getbintime(&start); + do { + TRACE(dev, DETAILS, "looking for end of busy\n"); + err = mmcspi_do_spi_read(dev, pollbuf, MMCSPI_POLL_LEN); + if (MMC_ERR_NONE != err) { + TRACE(dev, ERROR, "spi read failed\n"); + TRACE_EXIT(dev); + return (err); + } + + for (i = 0; i < MMCSPI_POLL_LEN; i++) { + if (pollbuf[i] != 0x00) { + TRACE(dev, DETAILS, + "end of busy found at %d\n", i); + break; + } + busy_length++; + } + + getbintime(&elapsed); + bintime_sub(&elapsed, &start); + + if (elapsed.sec > MMCSPI_TIMEOUT_SEC) { + TRACE(dev, ERROR, "card busy for longer than timeout\n"); + return (MMC_ERR_TIMEOUT); + } + } while (MMCSPI_POLL_LEN == i); + + TRACE(dev, RESULT, "busy for %u byte slots\n", busy_length); + + TRACE_EXIT(dev); + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_shift_copy(uint8_t *dest, uint8_t *src, unsigned int dest_len, + unsigned int shift) +{ + unsigned int i; + + if (0 == shift) { + memcpy(dest, src, dest_len); + } else { + for (i = 0; i < dest_len; i++) { + dest[i] = + (src[i] << shift) | + (src[i + 1] >> (8 - shift)); + } + } + + return (dest_len); +} + + +static unsigned int +mmcspi_get_response_token(device_t dev, uint8_t mask, uint8_t value, + unsigned int len, unsigned int has_busy, + uint8_t *rspbuf) +{ + int i; + unsigned int err; + unsigned int offset; + unsigned int shift = 0; + unsigned int remaining; + boolean_t found; + uint8_t pollbuf[2 * MMCSPI_MAX_RSP_LEN]; + struct bintime start, elapsed; + + TRACE_ENTER(dev); + + /* + * This loop searches data clocked out of the card for a response + * token matching the given mask and value. It will locate tokens + * that are not byte-aligned, as some cards send non-byte-aligned + * response tokens in some situations. For example, the following + * card consistently sends an unaligned response token to the stop + * command used to terminate multi-block reads: + * + * Transcend 2GB SDSC card, cid: + * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 + */ + + offset = 0; + found = false; + getbintime(&start); + do { + TRACE(dev, DETAILS, + "looking for response token with mask 0x%02x, value 0x%02x\n", + mask, value); + err = mmcspi_do_spi_read(dev, &pollbuf[offset], len); + if (MMC_ERR_NONE != err) { + TRACE(dev, ERROR, "spi read of response token failed\n"); + TRACE_EXIT(dev); + return (err); + } + + + for (i = 0; i < len + offset; i++) { + if ((pollbuf[i] & mask) == value) { + TRACE(dev, DETAILS, + "response token found at %d (0x%02x)\n", + i, pollbuf[i]); + shift = 0; + found = true; + break; + } else if (i < len + offset - 1) { + /* + * Not the last byte in the buffer, so check + * for a non-aligned response. + */ + uint16_t search_space = + ((uint16_t)pollbuf[i] << 8) | pollbuf[i + 1]; + uint16_t search_mask = (uint16_t)mask << 8; + uint16_t search_value = (uint16_t)value << 8; + + TRACE(dev, DETAILS, + "search: space=0x%04x mask=0x%04x val=0x%04x\n", + search_space, search_mask, search_value); + + for (shift = 1; shift < 8; shift++) { + search_space <<= 1; + if ((search_space & search_mask) == + search_value) { + found = true; + TRACE(dev, DETAILS, + "Found match at shift %u\n", + shift); + break; + } + } + + if (shift < 8) { + break; + } + } else { + /* + * Move the last byte to the first position + * and go 'round again. + */ + pollbuf[0] = pollbuf[i]; + } + } + + if (!found) { + offset = 1; + + getbintime(&elapsed); + bintime_sub(&elapsed, &start); + + if (elapsed.sec > MMCSPI_TIMEOUT_SEC) { + TRACE(dev, ERROR, + "timeout while looking for reponse token\n"); + return (MMC_ERR_TIMEOUT); + } + } + } while (!found); + + /* + * Note that if i == 0 and offset == 1, shift is always greater than + * zero. + */ + remaining = i - offset + (shift ? 1 : 0); + + TRACE(dev, DETAILS, "len=%u i=%u rem=%u shift=%u\n", + len, i, remaining, shift); + + if (remaining) { + err = mmcspi_do_spi_read(dev, &pollbuf[len + offset], remaining); + if (MMC_ERR_NONE != err) { + TRACE(dev, ERROR, + "spi read of remainder of response token failed\n"); + TRACE_EXIT(dev); + return (err); + } + } + + mmcspi_shift_copy(rspbuf, &pollbuf[i], len, shift); + + if (TRACE_ZONE_ENABLED(RESULT)) { + TRACE(dev, RESULT, "response ="); + for (i = 0; i < len; i++) { + printf(" 0x%02x", rspbuf[i]); + } + printf("\n"); + } + + if (has_busy) { + err = mmcspi_wait_for_not_busy(dev); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + } + + TRACE_EXIT(dev); + + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_set_up_command(device_t dev, struct mmcspi_command *mmcspi_cmd, + struct mmc_command *mmc_cmd) +{ + struct mmcspi_softc *sc = device_get_softc(dev); + struct mmcspi_slot *slot = &sc->slot; + uint32_t opcode = mmc_cmd->opcode; + uint32_t arg = mmc_cmd->arg; + uint32_t flags = mmc_cmd->flags; + uint32_t retries = mmc_cmd->retries; + unsigned char rsp_type; + unsigned char rsp_len; + unsigned int ldata_len = 0; + unsigned int use_crc = slot->crc_enabled; + + if (flags & MMC_CMD_IS_APP) { + switch (opcode) { + case ACMD_SD_STATUS: + rsp_type = MMCSPI_RSP_R2; + break; + case ACMD_SEND_NUM_WR_BLOCKS: + case ACMD_SET_WR_BLK_ERASE_COUNT: + case ACMD_SET_CLR_CARD_DETECT: + case ACMD_SEND_SCR: + rsp_type = MMCSPI_RSP_R1; + break; + case ACMD_SD_SEND_OP_COND: + /* only HCS bit is valid in spi mode */ + arg &= 0x40000000; + rsp_type = MMCSPI_RSP_R1; + break; + default: + TRACE(dev, ERROR, "Invalid app command opcode %u\n", + opcode); + return (MMC_ERR_INVALID); + } + } else { + switch (opcode) { + case MMC_GO_IDLE_STATE: + use_crc = 1; + rsp_type = MMCSPI_RSP_R1; + break; + + case MMC_SEND_OP_COND: + case MMC_SWITCH_FUNC: /* also SD_SWITCH_FUNC */ + case MMC_SET_BLOCKLEN: + case MMC_READ_SINGLE_BLOCK: + case MMC_READ_MULTIPLE_BLOCK: + case MMC_WRITE_BLOCK: + case MMC_WRITE_MULTIPLE_BLOCK: + case MMC_PROGRAM_CSD: + case MMC_SEND_WRITE_PROT: + case SD_ERASE_WR_BLK_START: + case SD_ERASE_WR_BLK_END: + case MMC_LOCK_UNLOCK: + case MMC_GEN_CMD: + case MMCSPI_CRC_ON_OFF: + rsp_type = MMCSPI_RSP_R1; + break; + + case MMC_SEND_CSD: + case MMC_SEND_CID: + arg = 0; /* no rca in spi mode */ + rsp_type = MMCSPI_RSP_R1; + ldata_len = 16; + break; + + case MMC_APP_CMD: + arg = 0; /* no rca in spi mode */ + rsp_type = MMCSPI_RSP_R1; + break; + + case MMC_STOP_TRANSMISSION: + case MMC_SET_WRITE_PROT: + case MMC_CLR_WRITE_PROT: + case MMC_ERASE: + rsp_type = MMCSPI_RSP_R1B; + break; + + case MMC_ALL_SEND_CID: + /* handle MMC_ALL_SEND_CID as MMC_SEND_CID */ + opcode = MMC_SEND_CID; + rsp_type = MMCSPI_RSP_R1; + ldata_len = 16; + break; + + case MMC_SEND_STATUS: + arg = 0; /* no rca in spi mode */ + rsp_type = MMCSPI_RSP_R2; + break; + + + case MMCSPI_READ_OCR: + rsp_type = MMCSPI_RSP_R3; + break; + + case SD_SEND_RELATIVE_ADDR: + /* + * Handle SD_SEND_RELATIVE_ADDR as MMC_SEND_STATUS - + * the rca returned to the caller will always be 0. + */ + opcode = MMC_SEND_STATUS; + rsp_type = MMCSPI_RSP_R2; + break; + + case SD_SEND_IF_COND: + use_crc = 1; + rsp_type = MMCSPI_RSP_R7; + break; + + default: + TRACE(dev, ERROR, "Invalid command opcode %u\n", opcode); + return (MMC_ERR_INVALID); + } + } + + switch (rsp_type) { + case MMCSPI_RSP_R1: + case MMCSPI_RSP_R1B: rsp_len = 1; break; + + case MMCSPI_RSP_R2: rsp_len = 2; break; + + case MMCSPI_RSP_R3: + case MMCSPI_RSP_R7: rsp_len = 5; break; + + default: + TRACE(dev, ERROR, "Unknown response type %u\n", rsp_type); + return (MMC_ERR_INVALID); + } + + mmcspi_cmd->mmc_cmd = mmc_cmd; + + mmcspi_cmd->opcode = opcode; + mmcspi_cmd->arg = arg; + mmcspi_cmd->flags = flags; + mmcspi_cmd->retries = retries; + mmcspi_cmd->use_crc = use_crc; + mmcspi_cmd->error_mask = MMCSPI_R1_ERR_MASK; + if (mmcspi_cmd->use_crc) { + mmcspi_cmd->error_mask &= ~MMCSPI_R1_CRC_ERR; + } + mmcspi_cmd->rsp_type = rsp_type; + mmcspi_cmd->rsp_len = rsp_len; + + memset(&mmcspi_cmd->ldata, 0, sizeof(struct mmc_data)); + mmcspi_cmd->ldata.len = ldata_len; + if (ldata_len) { + mmcspi_cmd->ldata.data = sc->slot.ldata_buf; + mmcspi_cmd->ldata.flags = MMC_DATA_READ; + + mmcspi_cmd->data = &mmcspi_cmd->ldata; + } else { + mmcspi_cmd->data = mmc_cmd->data; + } + + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_send_cmd(device_t dev, struct mmcspi_command *cmd, uint8_t *rspbuf) +{ + TRACE_ENTER(dev); + + unsigned int err; + uint32_t opcode = cmd->opcode; + uint32_t arg = cmd->arg; + uint8_t txbuf[8]; + uint8_t crc; + + TRACE(dev, ACTION, "sending %sMD%u(0x%08x)\n", + cmd->flags & MMC_CMD_IS_APP ? "AC": "C", opcode, arg); + + /* + * Sending this byte ahead of each command prevents some cards from + * responding with unaligned data, and doesn't bother the others. + * Examples: + * + * Sandisk 32GB SDHC card, cid: + * mid=0x03 oid=0x5344 pnm="SU32G" prv=8.0 mdt=00.2000 + */ + txbuf[0] = 0xff; + + txbuf[1] = 0x40 | (opcode & 0x3f); + txbuf[2] = arg >> 24; + txbuf[3] = (arg >> 16) & 0xff; + txbuf[4] = (arg >> 8) & 0xff; + txbuf[5] = arg & 0xff; + + if (cmd->use_crc) { + crc = update_crc7(CRC7_INITIAL, &txbuf[1], 5); + } else { + crc = 0; + } + + txbuf[6] = (crc << 1) | 0x01; + + /* + * Some cards have garbage on the bus in the first byte slot after + * the last command byte. This seems to be common with the stop + * command. Clocking out an extra byte with the command will + * result in that data not being searched for the response token, + * which is ok, because no cards respond that fast. + */ + txbuf[7] = 0xff; + + err = mmcspi_do_spi_write(dev, txbuf, sizeof(txbuf), NULL, 0); + if (MMC_ERR_NONE != err) { + TRACE(dev, ERROR, "spi write of command failed\n"); + TRACE_EXIT(dev); + return (err); + } + + TRACE(dev, DETAILS, + "rx cmd bytes 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + junkbuf[0], junkbuf[1], junkbuf[2], junkbuf[3], junkbuf[4], + junkbuf[5] ); + TRACE(dev, DETAILS, "skipped response byte is 0x%02x\n", junkbuf[6]); + + err = mmcspi_get_response_token(dev, MMCSPI_R1_MASK, MMCSPI_R1_VALUE, + cmd->rsp_len, + MMCSPI_RSP_R1B == cmd->rsp_type, rspbuf); + + if (MMC_ERR_NONE == err) { + if (rspbuf[0] & cmd->error_mask & MMCSPI_R1_CRC_ERR) { + err = MMC_ERR_BADCRC; + } else if (rspbuf[0] & cmd->error_mask) { + err = MMC_ERR_INVALID; + } + } + + TRACE_EXIT(dev); + return (err); +} + + +static unsigned int +mmcspi_read_block(device_t dev, uint8_t *data, unsigned int len, + unsigned int check_crc16, unsigned int check_crc7) +{ + TRACE_ENTER(dev); + + int i; + unsigned int err; + unsigned int non_token_bytes; + unsigned int data_captured; + unsigned int crc_captured; + unsigned int pollbufpos; + unsigned int crc16_mismatch = 0; + uint16_t crc16, computed_crc16; + uint8_t crc7, computed_crc7; + uint8_t pollbuf[MMCSPI_POLL_LEN]; + uint8_t crcbuf[MMCSPI_DATA_CRC_LEN]; + struct bintime start; + struct bintime elapsed; + + TRACE(dev, ACTION, "read block(%u)\n", len); + + /* + * With this approach, we could pointlessly read up to + * (MMCSPI_POLL_LEN - 3 - len) bytes from the spi bus, but only in + * the odd situation where MMCSPI_POLL_LEN is greater than len + 3. + */ + getbintime(&start); + do { + TRACE(dev, DETAILS, "looking for read token\n"); + err = mmcspi_do_spi_read(dev, pollbuf, MMCSPI_POLL_LEN); + if (MMC_ERR_NONE != err) { + TRACE(dev, ERROR, "token read on spi failed\n"); + TRACE_EXIT(dev); + return (err); + } + + for (i = 0; i < MMCSPI_POLL_LEN; i++) { + if (MMCSPI_TOKEN_SB == pollbuf[i]) { + TRACE(dev, RESULT, + "found start block token at %d\n", i); + break; + } else if (MMCSPI_IS_DE_TOKEN(pollbuf[i])) { + TRACE(dev, ERROR, + "found data error token at %d\n", i); + TRACE_EXIT(dev); + return (MMC_ERR_FAILED); + } + } + + getbintime(&elapsed); + bintime_sub(&elapsed, &start); + + if (elapsed.sec > MMCSPI_TIMEOUT_SEC) { + TRACE(dev, ERROR, + "timeout while looking for read token\n"); + return (MMC_ERR_TIMEOUT); + } + } while (MMCSPI_POLL_LEN == i); + + /* copy any data captured in tail of poll buf to data buf */ + non_token_bytes = MMCSPI_POLL_LEN - i - 1; + data_captured = min(non_token_bytes, len); + crc_captured = non_token_bytes - data_captured; + pollbufpos = i + 1; + + TRACE(dev, DETAILS, "data bytes captured in pollbuf = %u\n", + data_captured); + + memcpy(data, &pollbuf[pollbufpos], data_captured); + pollbufpos += data_captured; + + TRACE(dev, DETAILS, "data bytes to read = %u, crc_captured = %u\n", + len - data_captured, crc_captured); + + /* get any remaining data from the spi bus */ + if (data_captured < len) { + err = mmcspi_do_spi_read(dev, &data[data_captured], + len - data_captured); + if (MMC_ERR_NONE != err) { + TRACE(dev, ERROR, + "spi read of remainder of block failed\n"); + TRACE_EXIT(dev); + return (err); + } + + } + + /* copy any crc captured in the poll buf to the crc buf */ + memcpy(crcbuf, &pollbuf[pollbufpos], crc_captured); + + /* get any remaining crc */ + if (crc_captured < MMCSPI_DATA_CRC_LEN) { + TRACE(dev, DETAILS, "crc bytes to read = %u\n", + MMCSPI_DATA_CRC_LEN - crc_captured); + + err = mmcspi_do_spi_read(dev, &crcbuf[crc_captured], + MMCSPI_DATA_CRC_LEN - crc_captured); + if (MMC_ERR_NONE != err) { + TRACE(dev, ERROR, "spi read of crc failed\n"); + TRACE_EXIT(dev); + return (err); + } + } + + + /* + * The following crc checking code is deliberately structured to + * both require a passing crc-7 check and allow a passing crc-7 + * check to override a failing crc-16 check when crc-7 checking is + * enabled. + */ + if (check_crc16) { + crc16 = ((uint16_t)crcbuf[0] << 8) | crcbuf[1]; + computed_crc16 = update_crc16(CRC16_INITIAL, data, len); + TRACE(dev, RESULT, "sent_crc16=0x%04x computed_crc16=0x%04x\n", + crc16, computed_crc16); + + if (computed_crc16 != crc16) { + crc16_mismatch = 1; + + TRACE(dev, ERROR, + "crc16 mismatch, should be 0x%04x, is 0x%04x\n", + crc16, computed_crc16); + + if (!check_crc7) { + TRACE_EXIT(dev); + return (MMC_ERR_BADCRC); + } + } + } + + if (check_crc7) { + if (crc16_mismatch) { + /* + * Let the user know something else is being checked + * after announcing an error above. + */ + TRACE(dev, ERROR, "checking crc7\n"); + } + + crc7 = data[len - 1] >> 1; + computed_crc7 = update_crc7(CRC7_INITIAL, data, len - 1); + TRACE(dev, RESULT, "sent_crc7=0x%02x computed_crc7=0x%02x\n", + crc7, computed_crc7); + + if (computed_crc7 != crc7) { + TRACE(dev, ERROR, + "crc7 mismatch, should be 0x%02x, is 0x%02x\n", + crc7, computed_crc7); + + TRACE_EXIT(dev); + return (MMC_ERR_BADCRC); + } + } + + TRACE_EXIT(dev); + + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_send_stop(device_t dev, unsigned int retries) +{ + TRACE_ENTER(dev); + int i; + unsigned int err; + struct mmcspi_command stop; + struct mmc_command mmc_stop; + uint8_t stop_response; + + memset(&mmc_stop, 0, sizeof(mmc_stop)); + mmc_stop.opcode = MMC_STOP_TRANSMISSION; + mmc_stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + + err = mmcspi_set_up_command(dev, &stop, &mmc_stop); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + /* + * Retry stop commands that fail due to bad crc here because having + * the caller retry the entire read/write command due to such a + * failure is pointlessly expensive. + */ + for (i = 0; i <= retries; i++) { + TRACE(dev, ACTION, "sending stop message\n"); + + err = mmcspi_send_cmd(dev, &stop, &stop_response); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + TRACE(dev, RESULT, "stop response=0x%02x\n", stop_response); + + /* retry on crc error */ + if (stop_response & stop.error_mask & MMCSPI_R1_CRC_ERR) { + continue; + } + } + + if (stop_response & stop.error_mask) { + TRACE_EXIT(dev); + + /* + * Don't return MMC_ERR_BADCRC here, even if + * MMCSPI_R1_CRC_ERR is set, because that would trigger the + * caller's retry-on-crc-error mechanism, effectively + * squaring the maximum number of retries of the stop + * command. + */ + return (MMC_ERR_FAILED); + } + TRACE_EXIT(dev); + + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_read_phase(device_t dev, struct mmcspi_command *cmd) +{ + TRACE_ENTER(dev); + + struct mmc_data *data = cmd->data; + uint8_t *data8; + unsigned int data_offset; + unsigned int num_blocks; + unsigned int len; + unsigned int err; + int i; + + data8 = (uint8_t *)data->data; + data_offset = 0; + + if (data->len < MMCSPI_DATA_BLOCK_LEN) { + num_blocks = 1; + len = data->len; + } else { + num_blocks = data->len / MMCSPI_DATA_BLOCK_LEN; + len = MMCSPI_DATA_BLOCK_LEN; + } + + for (i = 0; i < num_blocks; i++) { + + /* + * The CID and CSD data blocks contain both a trailing crc-7 + * inside the data block and the standard crc-16 following + * the data block, so both are checked when use_crc is true. + * + * When crc checking has been enabled via CMD59, some cards + * send CID and CSD data blocks with correct crc-7 values + * but incorrect crc-16 values. read_block will accept + * those responses as valid as long as the crc-7 is correct. + * + * Examples: + * + * Super Talent 1GB SDSC card, cid: + * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=02.2010 + */ + err = mmcspi_read_block(dev, &data8[data_offset], len, + cmd->use_crc, + cmd->use_crc && + ((MMC_SEND_CID == cmd->opcode) || + (MMC_SEND_CSD == cmd->opcode))); + + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + data_offset += MMCSPI_DATA_BLOCK_LEN; + } + + /* multi-block read commands require a stop */ + if (num_blocks > 1) { + err = mmcspi_send_stop(dev, cmd->retries); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + } + + TRACE_EXIT(dev); + + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_write_block(device_t dev, uint8_t *data, unsigned int is_multi, + unsigned char use_crc, uint8_t *status) +{ + TRACE_ENTER(dev); + + unsigned int err; + uint16_t crc; + uint8_t response_token; + uint8_t txbuf[max(MMCSPI_POLL_LEN, 2)]; + + if (use_crc) { + crc = update_crc16(CRC16_INITIAL, data, MMCSPI_DATA_BLOCK_LEN); + } else { + crc = 0; + } + + TRACE(dev, ACTION, "write block(512) crc=0x%04x\n", crc); + + txbuf[0] = is_multi ? MMCSPI_TOKEN_SB_WM : MMCSPI_TOKEN_SB; + err = mmcspi_do_spi_write(dev, txbuf, 1, data, MMCSPI_DATA_BLOCK_LEN); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + txbuf[0] = crc >> 8; + txbuf[1] = crc & 0xff; + err = mmcspi_do_spi_write(dev, txbuf, 2, NULL, 0); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + err = mmcspi_get_response_token(dev, MMCSPI_DR_MASK, MMCSPI_DR_VALUE, + 1, 1, &response_token); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + *status = response_token & MMCSPI_DR_ERR_MASK; + + TRACE_EXIT(dev); + + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_write_phase(device_t dev, struct mmcspi_command *cmd) +{ + TRACE_ENTER(dev); + + struct mmc_data *data = cmd->data; + uint8_t *data8; + unsigned int data_offset; + unsigned int num_blocks; + unsigned int err; + int i; + uint8_t token[2]; + uint8_t status; + + data8 = (uint8_t *)data->data; + data_offset = 0; + num_blocks = data->len / MMCSPI_DATA_BLOCK_LEN; + for (i = 0; i < num_blocks; i++) { + err = mmcspi_write_block(dev, &data8[data_offset], + num_blocks > 1, cmd->use_crc, &status); + + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + if (MMCSPI_DR_ERR_NONE != status) { + if (num_blocks > 1) { + /* + * Ignore any failure reported for the stop + * command, as the return status for the + * write phase will be whatever error was + * indicated in the data respone token. + */ + mmcspi_send_stop(dev, cmd->retries); + } + + /* + * A CRC error can't be ignored here, even if crc + * use is disabled, as there is no way to simply + * carry on when a data error token has been sent. + */ + if (MMCSPI_DR_ERR_CRC == status) { + TRACE_EXIT(dev); + return (MMC_ERR_BADCRC); + } else { + TRACE_EXIT(dev); + return (MMC_ERR_FAILED); + } + } + + data_offset += MMCSPI_DATA_BLOCK_LEN; + } + + /* successful multi-block write commands require a stop token */ + if (num_blocks > 1) { + TRACE(dev, ACTION, "Sending stop token\n"); + + /* + * Most/all cards are a bit sluggish in assserting busy + * after receipt of the STOP_TRAN token. Clocking out an + * extra byte here provides a byte of dead time before + * looking for not busy, avoiding a premature not-busy + * determination with such cards. + */ + token[0] = MMCSPI_TOKEN_ST; + token[1] = 0xff; + + err = mmcspi_do_spi_write(dev, token, sizeof(token), NULL, 0); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + err = mmcspi_wait_for_not_busy(dev); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + } + + TRACE_EXIT(dev); + + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_translate_response(device_t dev, struct mmcspi_command *cmd, + uint8_t *rspbuf) +{ + TRACE_ENTER(dev); + + struct mmc_command *mmc_cmd = cmd->mmc_cmd; + uint32_t mmc_rsp_type = MMC_RSP(cmd->flags); + uint8_t *ldata = cmd->ldata.data; + + TRACE(dev, ACTION, "translating SPI rsp %u to SD rsp %u\n", + cmd->rsp_type, mmc_rsp_type); + + if ((MMC_RSP_R1 == mmc_rsp_type) || + (MMC_RSP_R1B == mmc_rsp_type)) { + + TRACE(dev, ACTION, "translating SPI-R1/2 to SD-R1\n"); + + if ((MMCSPI_RSP_R1 == cmd->rsp_type) || + (MMCSPI_RSP_R1B == cmd->rsp_type) || + (MMCSPI_RSP_R2 == cmd->rsp_type)) { + mmc_cmd->resp[0] = 0; + + if (rspbuf[0] & MMCSPI_R1_PARAM_ERR) + mmc_cmd->resp[0] |= R1_OUT_OF_RANGE; + + if (rspbuf[0] & MMCSPI_R1_ADDR_ERR) + mmc_cmd->resp[0] |= R1_ADDRESS_ERROR; + + if (rspbuf[0] & MMCSPI_R1_ERASE_ERR) + mmc_cmd->resp[0] |= R1_ERASE_SEQ_ERROR; + + if (rspbuf[0] & MMCSPI_R1_CRC_ERR) + mmc_cmd->resp[0] |= R1_COM_CRC_ERROR; + + if (rspbuf[0] & MMCSPI_R1_ILL_CMD) + mmc_cmd->resp[0] |= R1_ILLEGAL_COMMAND; + + if (rspbuf[0] & MMCSPI_R1_ERASE_RST) + mmc_cmd->resp[0] |= R1_ERASE_RESET; + + if (rspbuf[0] & MMCSPI_R1_IDLE) { + mmc_cmd->resp[0] |= (uint32_t)R1_STATE_IDLE << 9; + } else { + mmc_cmd->resp[0] |= (uint32_t)R1_STATE_READY << 9; + } + + /* When MMC_CMD_IS_APP is sent, emulate R1_APP_CMD + SD-bus status bit. */ + if (!(cmd->flags & MMC_CMD_IS_APP) && + (MMC_APP_CMD == cmd->opcode)) { + mmc_cmd->resp[0] |= R1_APP_CMD; + } + + if (MMCSPI_RSP_R2 == cmd->rsp_type) { + if (rspbuf[1] & MMCSPI_R2_OOR_CSD_OW) + mmc_cmd->resp[0] |= + R1_OUT_OF_RANGE | + R1_CSD_OVERWRITE; + + if (rspbuf[1] & MMCSPI_R2_ERASE_PARAM) + mmc_cmd->resp[0] |= R1_ERASE_PARAM; + + if (rspbuf[1] & MMCSPI_R2_WP_VIOLATE) + mmc_cmd->resp[0] |= R1_WP_VIOLATION; + + if (rspbuf[1] & MMCSPI_R2_ECC_FAIL) + mmc_cmd->resp[0] |= R1_CARD_ECC_FAILED; + + if (rspbuf[1] & MMCSPI_R2_CC_ERR) + mmc_cmd->resp[0] |= R1_CC_ERROR; + + if (rspbuf[1] & MMCSPI_R2_ERR) + mmc_cmd->resp[0] |= R1_ERROR; + + if (rspbuf[1] & MMCSPI_R2_WP_ER_LCK) + mmc_cmd->resp[0] |= + R1_LOCK_UNLOCK_FAILED | + R1_WP_ERASE_SKIP; + + if (rspbuf[1] & MMCSPI_R2_LOCKED) + mmc_cmd->resp[0] |= R1_CARD_IS_LOCKED; + + } + + } else { + return (MMC_ERR_INVALID); + } + + } else if (MMC_RSP_R2 == mmc_rsp_type) { + + if (16 == cmd->ldata.len) { + + TRACE(dev, ACTION, "translating SPI-R1/ldata(16) to SD-R2\n"); + + /* ldata contains bits 127:0 of the spi response */ + + mmc_cmd->resp[0] = + (uint32_t)ldata[0] << 24 | + (uint32_t)ldata[1] << 16 | + (uint32_t)ldata[2] << 8 | + (uint32_t)ldata[3]; + + mmc_cmd->resp[1] = + (uint32_t)ldata[4] << 24 | + (uint32_t)ldata[5] << 16 | + (uint32_t)ldata[6] << 8 | + (uint32_t)ldata[7]; + + mmc_cmd->resp[2] = + (uint32_t)ldata[8] << 24 | + (uint32_t)ldata[9] << 16 | + (uint32_t)ldata[10] << 8 | + (uint32_t)ldata[11]; + + mmc_cmd->resp[3] = + (uint32_t)ldata[12] << 24 | + (uint32_t)ldata[13] << 16 | + (uint32_t)ldata[14] << 8; + + } else { + return (MMC_ERR_INVALID); + } + + } else if (MMC_RSP_R3 == mmc_rsp_type) { + + if (MMCSPI_RSP_R3 == cmd->rsp_type) { + + TRACE(dev, ACTION, "translating SPI-R3 to SD-R3\n"); + + /* rspbuf contains a 40-bit spi-R3 from the + MMCSPI_READ_OCR response, of which bits 31:0 are + the OCR value */ + + /* spi response bits 31:0 mapped to + sdhc register bits 31:0 */ + mmc_cmd->resp[0] = + (uint32_t)rspbuf[1] << 24 | + (uint32_t)rspbuf[2] << 16 | + (uint32_t)rspbuf[3] << 8 | + (uint32_t)rspbuf[4]; + + /* Clear card busy bit (indicating busy) if the + SPI-R1 idle bit is set. */ + if (rspbuf[0] & MMCSPI_R1_IDLE) { + mmc_cmd->resp[0] &= ~MMC_OCR_CARD_BUSY; + } else { + mmc_cmd->resp[0] |= MMC_OCR_CARD_BUSY; + } + + TRACE(dev, DETAILS, "ocr=0x%08x\n", mmc_cmd->resp[0]); + } else { + return (MMC_ERR_INVALID); + } + + } else if (MMC_RSP_R7 == mmc_rsp_type) { + /* Note MMC_RSP_R6 and MMC_RSP_R7 are numerically equal, so + this handles both cases. */ + + if (MMCSPI_RSP_R2 == cmd->rsp_type) { + + TRACE(dev, ACTION, "translating SPI-R2 to SD-R6\n"); + + /* rca returned will always be zero */ + mmc_cmd->resp[0] = 0; + + if (rspbuf[0] & MMCSPI_R1_CRC_ERR) + mmc_cmd->resp[0] |= 0x8000; + + if (rspbuf[0] & MMCSPI_R1_ILL_CMD) + mmc_cmd->resp[0] |= 0x4000; + + if (rspbuf[1] & MMCSPI_R2_ERR) + mmc_cmd->resp[0] |= 0x2000; + + if (rspbuf[0] & MMCSPI_R1_IDLE) { + mmc_cmd->resp[0] |= (uint32_t)R1_STATE_IDLE << 9; + } else { + mmc_cmd->resp[0] |= (uint32_t)R1_STATE_READY << 9; + } + + } else if (MMCSPI_RSP_R7 == cmd->rsp_type) { + + TRACE(dev, ACTION, "translating SPI-R7 to SD-R7\n"); + + /* rsp buf contains a 40-bit spi-R7, of which bits + 11:0 need to be transferred */ + + /* spi response bits 11:0 mapped to + sdhc register bits 11:0 */ + mmc_cmd->resp[0] = + (uint32_t)(rspbuf[3] & 0xf) << 8 | + (uint32_t)rspbuf[4]; + + } else { + return (MMC_ERR_INVALID); + } + + } else if (MMC_RSP_NONE != mmc_rsp_type) { + return (MMC_ERR_INVALID); + } + + TRACE_EXIT(dev); + + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_get_ocr(device_t dev, uint8_t *ocrbuf) +{ + TRACE_ENTER(dev); + + struct mmc_command mmc_cmd; + struct mmcspi_command cmd; + unsigned int err; + uint8_t r1_status; + uint8_t rspbuf[MMCSPI_MAX_RSP_LEN]; + + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + mmc_cmd.opcode = MMCSPI_READ_OCR; + mmc_cmd.flags = MMC_RSP_R3 | MMC_CMD_AC; + + err = mmcspi_set_up_command(dev, &cmd, &mmc_cmd); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + err = mmcspi_send_cmd(dev, &cmd, rspbuf); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + r1_status = rspbuf[0] & cmd.error_mask; + if (r1_status) { + if (r1_status & MMCSPI_R1_CRC_ERR) { + err = MMC_ERR_BADCRC; + } else { + err = MMC_ERR_INVALID; + } + + TRACE_EXIT(dev); + return (err); + } + + memcpy(ocrbuf, &rspbuf[1], MMCSPI_OCR_LEN); + + TRACE_EXIT(dev); + + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_set_crc_on_off(device_t dev, unsigned int crc_on) +{ + struct mmc_command mmc_cmd; + struct mmcspi_command cmd; + unsigned int err; + uint8_t r1_status; + uint8_t rspbuf[MMCSPI_MAX_RSP_LEN]; + + TRACE_ENTER(dev); + + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + mmc_cmd.opcode = MMCSPI_CRC_ON_OFF; + mmc_cmd.arg = crc_on ? 1 : 0; + mmc_cmd.flags = MMC_RSP_NONE | MMC_CMD_AC; + + err = mmcspi_set_up_command(dev, &cmd, &mmc_cmd); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + err = mmcspi_send_cmd(dev, &cmd, rspbuf); + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + r1_status = rspbuf[0] & cmd.error_mask; + if (r1_status) { + if (r1_status & MMCSPI_R1_CRC_ERR) { + err = MMC_ERR_BADCRC; + } else { + err = MMC_ERR_INVALID; + } + + TRACE_EXIT(dev); + return (err); + } + + TRACE_EXIT(dev); + return (MMC_ERR_NONE); +} + + +static unsigned int +mmcspi_update_crc_setting(device_t dev, unsigned int crc_on) +{ + TRACE_ENTER(dev); + struct mmcspi_softc *sc = device_get_softc(dev); + struct mmcspi_slot *slot = &sc->slot; + unsigned int err; + int i; + + for (i = 0; i <= MMCSPI_RETRIES; i++) { + err = mmcspi_set_crc_on_off(dev, crc_on); + if (MMC_ERR_BADCRC != err) { + break; + } + } + + if (MMC_ERR_NONE != err) { + TRACE_EXIT(dev); + return (err); + } + + if (crc_on) { + slot->crc_enabled = 1; + } else { + slot->crc_enabled = 0; + } + + TRACE_EXIT(dev); + return (MMC_ERR_NONE); +} + + +static int +mmcspi_request(device_t brdev, device_t reqdev, struct mmc_request *req) +{ + TRACE_ENTER(brdev); + + struct mmcspi_softc *sc = device_get_softc(brdev); + struct mmcspi_slot *slot = &sc->slot; + struct mmcspi_command cmd; + struct mmc_command *mmc_cmd = req->cmd; + struct mmc_data *data; + unsigned int err; + unsigned int use_crc_sample; + int i, j; + uint32_t opcode; + uint32_t flags; + uint32_t last_opcode; + uint32_t last_flags; + uint8_t rspbuf[MMCSPI_MAX_RSP_LEN]; + +#define IS_CMD(code, cmd, flags) (!((flags) & MMC_CMD_IS_APP) && ((code) == (cmd))) +#define IS_ACMD(code, cmd, flags) (((flags) & MMC_CMD_IS_APP) && ((code) == (cmd))) + + if (power_on != slot->host.ios.power_mode) { + return (MMC_ERR_INVALID); + } + + /* + * Sample use_crc sysctl and adjust card setting if required and + * appropriate. + */ + use_crc_sample = sc->use_crc; + if (slot->crc_init_done && + (use_crc_sample != slot->crc_enabled)) { + err = mmcspi_update_crc_setting(brdev, use_crc_sample); + if (MMC_ERR_NONE != err) { + goto out; + } + slot->crc_init_done = 1; + } + + err = mmcspi_set_up_command(brdev, &cmd, mmc_cmd); + if (MMC_ERR_NONE != err) { + goto out; + } + + opcode = cmd.opcode; + flags = cmd.flags; + data = cmd.data; + + last_opcode = slot->last_opcode; + last_flags = slot->last_flags; + + /* enforce restrictions on request parameters */ + if (data) { + + /* + * All writes must be a multiple of the block length. All + * reads greater than the block length must be a multiple of + * the block length. + */ + if ((data->len % MMCSPI_DATA_BLOCK_LEN) && + !((data->flags & MMC_DATA_READ) && + (data->len < MMCSPI_DATA_BLOCK_LEN))) { + TRACE(brdev, ERROR, + "requested data phase not a multiple of %u\n", + MMCSPI_DATA_BLOCK_LEN); + err = MMC_ERR_INVALID; + goto out; + } + + if (((data->flags & MMC_DATA_READ) && + (data->flags & MMC_DATA_WRITE)) || + (data->flags & MMC_DATA_STREAM)) { + TRACE(brdev, ERROR, "illegal data phase flags 0x%02x\n", + data->flags); + err = MMC_ERR_INVALID; + goto out; + } + } + + for (i = 0; i <= cmd.retries; i++) { + /* + * On the next command following a CMD8, collect the OCR and + * save it off for use in the next ACMD41. + */ + if (IS_CMD(SD_SEND_IF_COND, last_opcode, last_flags)) { + err = mmcspi_get_ocr(brdev, slot->last_ocr); + if (MMC_ERR_NONE != err) { + if (MMC_ERR_BADCRC == err) { + continue; + } + goto out; + } + } + + err = mmcspi_send_cmd(brdev, &cmd, rspbuf); + if (MMC_ERR_NONE != err) { + if (MMC_ERR_BADCRC == err) { + continue; + } + goto out; + } + + if (data) { + if (data->flags & MMC_DATA_READ) { + err = mmcspi_read_phase(brdev, &cmd); + } else { /* MMC_DATA_WRITE */ + err = mmcspi_write_phase(brdev, &cmd); + } + + if (MMC_ERR_NONE != err) { + if (MMC_ERR_BADCRC == err) { + continue; + } + goto out; + } + } + + break; + } + + if (MMC_ERR_NONE != err) { + goto out; + } + + + /* + * If this was an ACMD_SD_SEND_OP_COND or MMC_SEND_OP_COND, we need + * to return an OCR value in the result. If the response from the + * card indicates it is still in the IDLE state, supply the OCR + * value obtained after the last CMD8, otherwise issue an + * MMCSPI_READ_OCR to get the current value, which will have a valid + * CCS bit. + * + * This dance is required under this emulation approach because the + * spec stipulates that no other commands should be sent while + * ACMD_SD_SEND_OP_COND is being used to poll for the end of the + * IDLE state, and some cards do enforce that requirement. + */ + if (IS_ACMD(ACMD_SD_SEND_OP_COND, opcode, flags) || + IS_CMD(MMC_SEND_OP_COND, opcode, flags)) { + + if (rspbuf[0] & MMCSPI_R1_IDLE) { + memcpy(&rspbuf[1], slot->last_ocr, MMCSPI_OCR_LEN); + } else { + + /* + * Some cards won't accept the MMCSPI_CRC_ON_OFF + * command until initialization is complete. + * + * Examples: + * + * Super Talent 1GB SDSC card, cid: + * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=02.2010 + */ + if (!slot->crc_init_done) { + err = mmcspi_update_crc_setting(brdev, + sc->use_crc); + if (MMC_ERR_NONE != err) { + goto out; + } + slot->crc_init_done = 1; + } + + for (j = 0; j <= cmd.retries; j++) { + /* + * Note that in this case, we pass on the R1 + * from READ_OCR. + */ + err = mmcspi_get_ocr(brdev, rspbuf); + if (MMC_ERR_NONE != err) { + if (MMC_ERR_BADCRC == err) { + continue; + } + + goto out; + } + + } + + if (MMC_ERR_NONE != err) { + goto out; + } + + } + + /* adjust the SPI response type to include the OCR */ + cmd.rsp_type = MMCSPI_RSP_R3; + } + + err = mmcspi_translate_response(brdev, &cmd, rspbuf); + if (MMC_ERR_NONE != err) { + goto out; + } + + out: + slot->last_opcode = mmc_cmd->opcode; + slot->last_flags = mmc_cmd->flags; + + mmc_cmd->error = err; + + if (req->done) { + req->done(req); + } + + TRACE_EXIT(brdev); + + return (err); +} + + +static int +mmcspi_get_ro(device_t brdev, device_t reqdev) +{ + TRACE_ENTER(brdev); + TRACE_EXIT(brdev); + + /* XXX no support for this currently */ + return (0); +} + + +static int +mmcspi_acquire_host(device_t brdev, device_t reqdev) +{ + TRACE_ENTER(brdev); + + struct mmcspi_softc *sc = device_get_softc(brdev); + struct mmcspi_slot *slot = device_get_ivars(reqdev); + struct spi_config cfg; + int err = 0; + + MMCSPI_LOCK_SLOT(slot); + while (slot->bus_busy) { + mtx_sleep(slot, &slot->mtx, 0, "mmcspiah", 0); + } + slot->bus_busy++; + MMCSPI_UNLOCK_SLOT(slot); + + SPIBUS_ACQUIRE_BUS(sc->busdev, sc->dev); + + /* Restore the clock to the last setting, as it may have been + changed by another spibus device. */ + cfg.clock_hz = slot->host.ios.clock; + SPIBUS_SET_CONFIG(slot->sc->busdev, &cfg); + + TRACE_EXIT(brdev); + + return (err); +} + + +static int +mmcspi_release_host(device_t brdev, device_t reqdev) +{ + TRACE_ENTER(brdev); + + struct mmcspi_softc *sc = device_get_softc(brdev); + struct mmcspi_slot *slot = device_get_ivars(reqdev); + + SPIBUS_RELEASE_BUS(sc->busdev, sc->dev); + + MMCSPI_LOCK_SLOT(slot); + slot->bus_busy--; + MMCSPI_UNLOCK_SLOT(slot); + + wakeup(slot); + + TRACE_EXIT(brdev); + + return (0); +} + + +static uint8_t +update_crc7(uint8_t crc, uint8_t *buf, unsigned int len) +{ + int i; + uint8_t tmp; + + for (i=0; i> 8) ^ c16; + crc = (crc << 8) ^ crc16tab[tmp]; + } + + return crc; +} + + +static void +init_crc7tab(void) +{ +#define P_CRC7 0x89 + + int i, j; + uint8_t crc, c; + + for (i=0; i<256; i++) { + + c = (uint8_t)i; + crc = (c & 0x80) ? c ^ P_CRC7 : c; + + for (j=1; j<8; j++) { + crc = crc << 1; + + if (crc & 0x80) { + crc = crc ^ P_CRC7; + } + } + + crc7tab[i] = crc; + } +} + + +static void +init_crc16tab(void) +{ +#define P_CCITT 0x1021 + + int i, j; + uint16_t crc, c; + + for (i=0; i<256; i++) { + + crc = 0; + c = ((uint16_t) i) << 8; + + for (j=0; j<8; j++) { + + if ((crc ^ c) & 0x8000) crc = ( crc << 1 ) ^ P_CCITT; + else crc = crc << 1; + + c = c << 1; + } + + crc16tab[i] = crc; + } +} + + +static int +mmcspi_modevent_handler(module_t mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + init_crc7tab(); + init_crc16tab(); + memset(onesbuf, 0xff, sizeof(onesbuf)); + break; + default: + return (EOPNOTSUPP); + } + + return (0); +} + + + +static device_method_t mmcspi_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, mmcspi_probe), + DEVMETHOD(device_attach, mmcspi_attach), + DEVMETHOD(device_detach, mmcspi_detach), + DEVMETHOD(device_suspend, mmcspi_suspend), + DEVMETHOD(device_resume, mmcspi_resume), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, mmcspi_read_ivar), + DEVMETHOD(bus_write_ivar, mmcspi_write_ivar), + + /* mmcbr_if */ + DEVMETHOD(mmcbr_update_ios, mmcspi_update_ios), + DEVMETHOD(mmcbr_request, mmcspi_request), + DEVMETHOD(mmcbr_get_ro, mmcspi_get_ro), + DEVMETHOD(mmcbr_acquire_host, mmcspi_acquire_host), + DEVMETHOD(mmcbr_release_host, mmcspi_release_host), + + {0, 0}, +}; + + +static driver_t mmcspi_driver = { + "mmcspi", + mmcspi_methods, + sizeof(struct mmcspi_softc), +}; +static devclass_t mmcspi_devclass; + + +DRIVER_MODULE(mmcspi, spibus, mmcspi_driver, mmcspi_devclass, mmcspi_modevent_handler, 0); + + + + +#if defined(MMCSPI_ENABLE_DEBUG_FUNCS) +static void +mmcspi_dump_data(device_t dev, const char *label, uint8_t *data, + unsigned int len) +{ + TRACE_ENTER(dev); + + unsigned int i, j; + unsigned int num_lines; + unsigned int residual; + + num_lines = len / 16; + residual = len - 16 * num_lines; + + for(i = 0; i < num_lines; i++) { + device_printf(dev, "%s:", label); + for(j = 0; j < 16; j++) { + printf(" %02x", data[i * 16 + j]); + } + printf("\n"); + } + + if (residual) { + device_printf(dev, "%s:", label); + for(j = 0; j < residual; j++) { + printf(" %02x", data[num_lines * 16 + j]); + } + printf("\n"); + } + + TRACE_EXIT(dev); +} + + +static void +mmcspi_dump_spi_bus(device_t dev, unsigned int len) +{ + TRACE_ENTER(dev); + + unsigned int num_blocks = len / MMCSPI_DATA_BLOCK_LEN; + unsigned int residual = len - num_blocks * MMCSPI_DATA_BLOCK_LEN; + unsigned int i; + + for(i = 0; i < num_blocks; i++) { + if (MMC_ERR_NONE != + mmcspi_do_spi_read(dev, junkbuf, MMCSPI_DATA_BLOCK_LEN)) { + device_printf(dev, "spi read failed\n"); + return; + } + + mmcspi_dump_data(dev, "bus_data", junkbuf, + MMCSPI_DATA_BLOCK_LEN); + } + + if (residual) { + if (MMC_ERR_NONE != + mmcspi_do_spi_read(dev, junkbuf, residual)) { + device_printf(dev, "spi read failed\n"); + return; + } + + mmcspi_dump_data(dev, "bus_data", junkbuf, residual); + } + + + TRACE_EXIT(dev); +} +#endif + + +/* + * These are the cards this code was tested with (initialization, + * identification, and single and multi block read and write with crc + * checking disabled and enabled), sorted by CID: + * + * Delkin Devices 2GB mid=0x02 oid=0x544d pnm="SA02G" prv=0.4 mdt=05.2010 + * cid=02544d5341303247049c10012e00a500 csd=002e00325b5aa3a9ffffff800a800000 + * + * Kingston 4GB mid=0x02 oid=0x544d pnm="SA04G" prv=0.9 mdt=12.2011 + * cid=02544d53413034470927fec2e000bc00 csd=400e00325b5900001da77f800a400000 + * + * PNY 8GB mid=0x02 oid=0x544d pnm="SA08G" prv=0.6 mdt=08.2011 + * cid=02544d53413038470615a63ff500b800 csd=400e00325b5900003b4f7f800a400000 + * + * Duracell 2GB mid=0x03 oid=0x5344 pnm="SU02G" prv=8.0 mdt=01.2011 + * cid=0353445355303247800c8a671800b100 csd=002600325f5a83aefefbcfff92804000 + * + * ADATA 4GB mid=0x03 oid=0x5344 pnm="SU04G" prv=8.0 mdt=08.2011 + * cid=03534453553034478002bc1eae00b800 csd=400e00325b5900001d8a7f800a404000 + * + * Sandisk 4GB mid=0x03 oid=0x5344 pnm="SU04G" prv=8.0 mdt=10.2010 + * cid=0353445355303447800accba9e00aa00 csd=400e00325b5900001d8a7f800a404000 + * + * PQI 4GB mid=0x03 oid=0x5344 pnm="SU04G" prv=8.0 mdt=01.2011 + * cid=0353445355303447800c981f7f00b100 csd=400e00325b5900001d8a7f800a404000 + * + * Silicon Power 4GB mid=0x03 oid=0x5344 pnm="SU04G" prv=8.0 mdt=11.2011 + * cid=035344535530344780136f39a800bb00 csd=400e00325b5900001d8a7f800a404000 + * + * Team Group 4GB mid=0x03 oid=0x5344 pnm="SU04G" prv=8.0 mdt=12.2011 + * cid=035344535530344780145d6e4500bc00 csd=400e00325b5900001d8a7f800a404000 + * + * Fujifilm 4GB mid=0x03 oid=0x5344 pnm="SU04G" prv=8.0 mdt=03.2011 + * cid=035344535530344780214a790500b300 csd=400e00325b5900001d8a7f800a404000 + * + * Patriot Memory 4GB mid=0x03 oid=0x5344 pnm="SU04G" prv=8.0 mdt=05.2011 + * cid=035344535530344780808539c000b500 csd=400e00325b5900001d8a7f800a404000 + * + * Sandisk 32GB mid=0x03 oid=0x5344 pnm="SU32G" prv=8.0 mdt=00.2000 + * cid=03534453553332478061bbfd6500bb00 csd=400e00325b590000edc87f800a404000 + * + * Sandisk 64GB mid=0x03 oid=0x5344 pnm="SU64G" prv=8.0 mdt=12.2011 + * cid=035344535536344780718fc1a400bc00 csd=400e00325b590001dbd37f800a404000 + * + * Dane-Elec 4GB mid=0x12 oid=0x3456 pnm="SMI " prv=1.0 mdt=10.2011 + * cid=123456534d4920201042020abd00ba00 csd=400e00325b5900001d6f7f800a400000 + * + * G.SKILL 4GB mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 + * cid=1b534d30303030301008101f4100bc00 csd=400e00325b5900001d9b7f800a400000 + * + * Transcend 2GB mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 + * cid=1b534d30303030301008d1f66900b300 csd=007fff325b5a83baf6dbdfff0e800000 + * + * Samsung 2GB mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=10.2011 + * cid=1b534d3030303030101f9ca96700ba00 csd=007fff325b5a83baf6dbdfff0e800000 + * + * Transcend 2GB mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 + * cid=1b534d30303030301029ef1c2100b300 csd=007fff325b5a83baf6dbdfff0e800000 + * + * Super Talent 1GB mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=02.2010 + * cid=1b534d303030303010b1c68e2100a200 csd=007fff325f5983cb76dbdfff96400000 + * + * Tribeca 1GB mid=0x1b oid=0x534d pnm="UD " prv=1.0 mdt=09.2007 + * cid=1b534d554420202010d3905704007900 csd=002f00325f5983ca6db7ff9f96400000 + * + * Verbatim 2GB mid=0x27 oid=0x5048 pnm="SD02G" prv=3.0 mdt=08.2011 + * cid=2750485344303247309d073a8f00b800 csd=005e00325b5aa3a9ffffff800a800000 + * + * Maxell 4GB mid=0x27 oid=0x5048 pnm="SD04G" prv=3.0 mdt=10.2011 + * cid=2750485344303447307c19a2f100ba00 csd=400e00325b5900001da77f800a400000 + * + * Mushkin 4GB mid=0x27 oid=0x5048 pnm="SD04G" prv=3.0 mdt=08.2011 + * cid=2750485344303447307cda40e800b800 csd=400e00325b5900001d6f7f800a400000 + * + * Sony 4GB mid=0x27 oid=0x5048 pnm="SD04G" prv=3.0 mdt=09.2011 + * cid=2750485344303447307ceadd4c00b900 csd=400e00325b5900001d6f7f800a400000 + * + * Wintec 16GB mid=0x27 oid=0x5048 pnm="SD16G" prv=3.0 mdt=10.2011 + * cid=2750485344313647307c2f8c9c00ba00 csd=400e00325b590000773f7f800a400000 + * + * Lexar 4GB mid=0x28 oid=0x4245 pnm="LEXAR" prv=1.0 mdt=10.2011 + * cid=2842454c4558415210be03d48100ba00 csd=400e00325b5900001ddd7f800a400000 + * + * HP 2GB mid=0x28 oid=0x4245 pnm="SDU2G" prv=0.0 mdt=07.2011 + * cid=2842455344553247000000144f00b700 csd=002f00325b5a83856db7ffbf16800000 + * + * EP Memory 4GB mid=0x36 oid=0x414f pnm="MSD " prv=1.0 mdt=07.2010 + * cid=36414f4d5344202010000085b700a700 csd=400e00325b5900001dff7f800a400000 + * + * RiDATA 4GB mid=0x73 oid=0x4247 pnm="NCard" prv=0.0 mdt=12.2011 + * cid=7342474e43617264000000035200bc00 csd=400e00325b5900001d997f800a400000 + * + * Pretec 8GB mid=0x73 oid=0x4247 pnm="NCard" prv=1.0 mdt=12.2011 + * cid=7342474e43617264102600146400bc00 csd=400e00325b5900003bdd7f800a400000 + * + * Transcend 8GB mid=0x74 oid=0x4a45 pnm="USD " prv=1.0 mdt=00.2000 + * cid=744a45555344202010051004a900bb00 csd=400e00325b5900003bbd7f800a400000 + * + */ Index: sys/dev/mmc/mmc.c =================================================================== --- sys/dev/mmc/mmc.c (revision 234999) +++ sys/dev/mmc/mmc.c (working copy) @@ -224,7 +224,7 @@ sc = device_get_softc(busdev); MMC_LOCK(sc); if (sc->owner) - panic("mmc: host bridge didn't seralize us."); + panic("mmc: host bridge didn't serialize us."); sc->owner = dev; MMC_UNLOCK(sc); @@ -369,6 +369,8 @@ struct mmc_command appcmd; int err = MMC_ERR_NONE, i; + cmd->flags |= MMC_CMD_IS_APP; + for (i = 0; i <= retries; i++) { appcmd.opcode = MMC_APP_CMD; appcmd.arg = rca << 16; @@ -549,9 +551,14 @@ { int flags; - flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC; - return (mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16, - flags, NULL, CMD_RETRIES)); + if (bus_type_spi == mmcbr_get_bus_type(sc->dev)) { + /* card selection isn't required */ + return (MMC_ERR_NONE); + } else { + flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC; + return (mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16, + flags, NULL, CMD_RETRIES)); + } } static int @@ -655,6 +662,11 @@ uint8_t value; u_char switch_res[64]; + if (bus_type_spi == mmcbr_get_bus_type(sc->dev)) { + /* bus timing can't be set on the card in spi mode */ + return (MMC_ERR_NONE); + } + switch (timing) { case bus_timing_normal: value = 0; @@ -859,7 +871,7 @@ if (v == 0) { m = mmc_get_bits(raw_csd, 128, 115, 4); e = mmc_get_bits(raw_csd, 128, 112, 3); - csd->tacc = exp[e] * mant[m] + 9 / 10; + csd->tacc = (exp[e] * mant[m] + 9) / 10; csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100; m = mmc_get_bits(raw_csd, 128, 99, 4); e = mmc_get_bits(raw_csd, 128, 96, 3); @@ -887,7 +899,7 @@ } else if (v == 1) { m = mmc_get_bits(raw_csd, 128, 115, 4); e = mmc_get_bits(raw_csd, 128, 112, 3); - csd->tacc = exp[e] * mant[m] + 9 / 10; + csd->tacc = (exp[e] * mant[m] + 9) / 10; csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100; m = mmc_get_bits(raw_csd, 128, 99, 4); e = mmc_get_bits(raw_csd, 128, 96, 3); @@ -1002,7 +1014,7 @@ } static int -mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcid) +mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcsd) { struct mmc_command cmd; int err; @@ -1012,7 +1024,7 @@ cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, 0); - memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t)); + memcpy(rawcsd, cmd.resp, 4 * sizeof(uint32_t)); return (err); } @@ -1121,6 +1133,36 @@ return (err); } +static int +mmc_send_status(struct mmc_softc *sc, uint16_t rca, uint32_t *status) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, 0); + *status = cmd.resp[0]; + return (err); +} + +static int +mmc_set_blocklen(struct mmc_softc *sc, uint32_t len) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = len; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, 0); + return (err); +} + + static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard) { @@ -1144,6 +1186,7 @@ struct mmc_ivars *ivar = NULL; device_t *devlist; int err, i, devcount, newcard; + uint32_t status; uint32_t raw_cid[4]; uint32_t resp, sec_count; device_t child; @@ -1194,6 +1237,11 @@ ivar->rca = resp >> 16; /* Get card CSD. */ mmc_send_csd(sc, ivar->rca, ivar->raw_csd); + if (bootverbose || mmc_debug) { + device_printf(sc->dev, "%sard detected (CSD %08x%08x%08x%08x)\n", + newcard ? "New c" : "C", + ivar->raw_csd[0], ivar->raw_csd[1], ivar->raw_csd[2], ivar->raw_csd[3]); + } mmc_decode_csd_sd(ivar->raw_csd, &ivar->csd); ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE; if (ivar->csd.csd_structure > 0) @@ -1201,6 +1249,17 @@ ivar->tran_speed = ivar->csd.tran_speed; ivar->erase_sector = ivar->csd.erase_sector * ivar->csd.write_bl_len / MMC_SECTOR_SIZE; + + err = mmc_send_status(sc, ivar->rca, &status); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, "Error reading card status %d\n", err); + break; + } + if (status & R1_CARD_IS_LOCKED) { + device_printf(sc->dev, "Card is password protected, skipping.\n"); + break; + } + /* Get card SCR. Card must be selected to fetch it. */ mmc_select_card(sc, ivar->rca); mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr); @@ -1228,7 +1287,23 @@ if ((mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) && (ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) ivar->bus_width = bus_width_4; + + /* + * Some cards that report maximum I/O block sizes + * greater than 512 require the block length to be + * set to 512, even though that is supposed to be + * the default. Examples: + * + * Transcend 2GB SDSC card, cid: + * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 + */ + if ((MMC_SECTOR_SIZE != ivar->csd.read_bl_len) || + (MMC_SECTOR_SIZE != ivar->csd.write_bl_len)) { + mmc_set_blocklen(sc, MMC_SECTOR_SIZE); + } + mmc_format_card_id_string(ivar); + if (bootverbose || mmc_debug) mmc_log_card(sc->dev, ivar, newcard); if (newcard) { @@ -1243,11 +1318,28 @@ mmc_set_relative_addr(sc, ivar->rca); /* Get card CSD. */ mmc_send_csd(sc, ivar->rca, ivar->raw_csd); + if (bootverbose || mmc_debug) { + device_printf(sc->dev, "%sard detected (CSD %08x%08x%08x%08x)\n", + newcard ? "New c" : "C", + ivar->raw_csd[0], ivar->raw_csd[1], ivar->raw_csd[2], ivar->raw_csd[3]); + } + mmc_decode_csd_mmc(ivar->raw_csd, &ivar->csd); ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE; ivar->tran_speed = ivar->csd.tran_speed; ivar->erase_sector = ivar->csd.erase_sector * ivar->csd.write_bl_len / MMC_SECTOR_SIZE; + + err = mmc_send_status(sc, ivar->rca, &status); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, "Error reading card status %d\n", err); + break; + } + if (status & R1_CARD_IS_LOCKED) { + device_printf(sc->dev, "Card is password protected, skipping.\n"); + break; + } + /* Only MMC >= 4.x cards support EXT_CSD. */ if (ivar->csd.spec_vers >= 4) { /* Card must be selected to fetch EXT_CSD. */ @@ -1286,7 +1378,22 @@ ivar->bus_width = bus_width_1; ivar->timing = bus_timing_normal; } + + /* + * Some cards that report maximum I/O block sizes greater + * than 512 require the block length to be set to 512, even + * though that is supposed to be the default. Examples: + * + * Transcend 2GB SDSC card, cid: + * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 + */ + if ((MMC_SECTOR_SIZE != ivar->csd.read_bl_len) || + (MMC_SECTOR_SIZE != ivar->csd.write_bl_len)) { + mmc_set_blocklen(sc, MMC_SECTOR_SIZE); + } + mmc_format_card_id_string(ivar); + if (bootverbose || mmc_debug) mmc_log_card(sc->dev, ivar, newcard); if (newcard) { @@ -1362,7 +1469,7 @@ err = mmc_send_if_cond(sc, 1); if ((bootverbose || mmc_debug) && err == 0) device_printf(sc->dev, "SD 2.0 interface conditions: OK\n"); - if (mmc_send_app_op_cond(sc, err ? 0 : MMC_OCR_CCS, &ocr) != + if (mmc_send_app_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { if (bootverbose || mmc_debug) device_printf(sc->dev, "SD probe: failed\n"); @@ -1584,3 +1691,4 @@ DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci, mmc_driver, mmc_devclass, NULL, NULL); +DRIVER_MODULE(mmc, mmcspi, mmc_driver, mmc_devclass, NULL, NULL); Index: sys/dev/sdhci/sdhci.c =================================================================== --- sys/dev/sdhci/sdhci.c (revision 234999) +++ sys/dev/sdhci/sdhci.c (working copy) @@ -1467,6 +1467,9 @@ switch (which) { default: return (EINVAL); + case MMCBR_IVAR_BUS_TYPE: + *result = bus_type_sd; + break; case MMCBR_IVAR_BUS_MODE: *result = slot->host.ios.bus_mode; break; @@ -1559,6 +1562,7 @@ case MMCBR_IVAR_TIMING: slot->host.ios.timing = value; break; + case MMCBR_IVAR_BUS_TYPE: case MMCBR_IVAR_CAPS: case MMCBR_IVAR_HOST_OCR: case MMCBR_IVAR_F_MIN: Index: sys/dev/spibus/spi.h =================================================================== --- sys/dev/spibus/spi.h (revision 234999) +++ sys/dev/spibus/spi.h (working copy) @@ -1,5 +1,9 @@ /* $FreeBSD$ */ +#define SPI_SKIP_CHIP_SELECT 0x0001 /* bus is already acquired */ +#define SPI_CHIP_SELECT_HIGH 0x0002 /* set chip select high for + duration of transfer */ + struct spi_command { void *tx_cmd; uint32_t tx_cmd_sz; @@ -9,4 +13,11 @@ uint32_t tx_data_sz; void *rx_data; uint32_t rx_data_sz; + uint16_t flags; }; + +struct spi_config { + unsigned int clock_hz; /* sending a 0 to spibus_set_config + requests the maximum possible + frequency */ +}; Index: sys/dev/spibus/spibus.c =================================================================== --- sys/dev/spibus/spibus.c (revision 234999) +++ sys/dev/spibus/spibus.c (working copy) @@ -22,6 +22,7 @@ static int spibus_probe(device_t dev) { + device_set_desc(dev, "spibus bus"); return (0); } @@ -60,6 +61,7 @@ static int spibus_suspend(device_t dev) { + return (bus_generic_suspend(dev)); } @@ -67,6 +69,7 @@ int spibus_resume(device_t dev) { + return (bus_generic_resume(dev)); } @@ -107,6 +110,7 @@ spibus_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { + *buf = '\0'; return (0); } @@ -155,12 +159,48 @@ resource_int_value(dname, dunit, "cs", &devi->cs); } +static void +spibus_acquire_bus_impl(device_t dev, device_t child) +{ + + SPIBUS_ACQUIRE_BUS(device_get_parent(dev), child); +} + +static void +spibus_release_bus_impl(device_t dev, device_t child) +{ + + SPIBUS_RELEASE_BUS(device_get_parent(dev), child); +} + static int spibus_transfer_impl(device_t dev, device_t child, struct spi_command *cmd) { + return (SPIBUS_TRANSFER(device_get_parent(dev), child, cmd)); } +static int +spibus_write_impl(device_t dev, uint32_t reg, uint32_t data) +{ + + return (SPIBUS_WRITE(device_get_parent(dev), reg, data)); +} + +static int +spibus_get_config_impl(device_t dev, struct spi_config *cfg) +{ + + return (SPIBUS_GET_CONFIG(device_get_parent(dev), cfg)); +} + +static int +spibus_set_config_impl(device_t dev, struct spi_config *cfg) +{ + + return (SPIBUS_SET_CONFIG(device_get_parent(dev), cfg)); +} + static device_method_t spibus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, spibus_probe), @@ -180,7 +220,12 @@ DEVMETHOD(bus_hinted_child, spibus_hinted_child), /* spibus interface */ + DEVMETHOD(spibus_acquire_bus, spibus_acquire_bus_impl), + DEVMETHOD(spibus_release_bus, spibus_release_bus_impl), DEVMETHOD(spibus_transfer, spibus_transfer_impl), + DEVMETHOD(spibus_write, spibus_write_impl), + DEVMETHOD(spibus_get_config, spibus_get_config_impl), + DEVMETHOD(spibus_set_config, spibus_set_config_impl), DEVMETHOD_END }; Index: sys/dev/spibus/spibus_if.m =================================================================== --- sys/dev/spibus/spibus_if.m (revision 234999) +++ sys/dev/spibus/spibus_if.m (working copy) @@ -32,6 +32,22 @@ INTERFACE spibus; # +# Acquire bus and select the device +# +METHOD void acquire_bus { + device_t dev; + device_t child; +}; + +# +# Release bus and deselect the device +# +METHOD void release_bus { + device_t dev; + device_t child; +}; + +# # Do a spi command # METHOD int transfer { @@ -39,3 +55,28 @@ device_t child; struct spi_command *cmd; }; + +# +# Write a command to spibus +# +METHOD int write { + device_t dev; + uint32_t reg; + uint32_t data; +}; + +# +# Get spibus configuration +# +METHOD int get_config { + device_t dev; + struct spi_config *cfg; +}; + +# +# Set spibus configuration +# +METHOD int set_config { + device_t dev; + struct spi_config *cfg; +};